test_platforms.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. from __future__ import absolute_import
  2. import errno
  3. import os
  4. import sys
  5. import signal
  6. from mock import Mock, patch, call
  7. from celery import _find_option_with_arg
  8. from celery import platforms
  9. from celery.five import open_fqdn
  10. from celery.platforms import (
  11. get_fdmax,
  12. ignore_errno,
  13. set_process_title,
  14. signals,
  15. maybe_drop_privileges,
  16. setuid,
  17. setgid,
  18. initgroups,
  19. parse_uid,
  20. parse_gid,
  21. detached,
  22. DaemonContext,
  23. create_pidlock,
  24. Pidfile,
  25. LockFailed,
  26. setgroups,
  27. _setgroups_hack,
  28. close_open_fds,
  29. )
  30. try:
  31. import resource
  32. except ImportError: # pragma: no cover
  33. resource = None # noqa
  34. from celery.tests.case import (
  35. Case, WhateverIO, override_stdouts, mock_open, SkipTest,
  36. )
  37. class test_find_option_with_arg(Case):
  38. def test_long_opt(self):
  39. self.assertEqual(
  40. _find_option_with_arg(['--foo=bar'], long_opts=['--foo']),
  41. 'bar'
  42. )
  43. def test_short_opt(self):
  44. self.assertEqual(
  45. _find_option_with_arg(['-f', 'bar'], short_opts=['-f']),
  46. 'bar'
  47. )
  48. class test_close_open_fds(Case):
  49. def test_closes(self):
  50. with patch('os.close') as _close:
  51. with patch('celery.platforms.get_fdmax') as fdmax:
  52. fdmax.return_value = 3
  53. close_open_fds()
  54. _close.assert_has_calls([call(2), call(1), call(0)])
  55. _close.side_effect = OSError()
  56. _close.side_effect.errno = errno.EBADF
  57. close_open_fds()
  58. class test_ignore_errno(Case):
  59. def test_raises_EBADF(self):
  60. with ignore_errno('EBADF'):
  61. exc = OSError()
  62. exc.errno = errno.EBADF
  63. raise exc
  64. def test_otherwise(self):
  65. with self.assertRaises(OSError):
  66. with ignore_errno('EBADF'):
  67. exc = OSError()
  68. exc.errno = errno.ENOENT
  69. raise exc
  70. class test_set_process_title(Case):
  71. def when_no_setps(self):
  72. prev = platforms._setproctitle = platforms._setproctitle, None
  73. try:
  74. set_process_title('foo')
  75. finally:
  76. platforms._setproctitle = prev
  77. class test_Signals(Case):
  78. @patch('signal.getsignal')
  79. def test_getitem(self, getsignal):
  80. signals['SIGINT']
  81. getsignal.assert_called_with(signal.SIGINT)
  82. def test_supported(self):
  83. self.assertTrue(signals.supported('INT'))
  84. self.assertFalse(signals.supported('SIGIMAGINARY'))
  85. def test_reset_alarm(self):
  86. if sys.platform == 'win32':
  87. raise SkipTest('signal.alarm not available on Windows')
  88. with patch('signal.alarm') as _alarm:
  89. signals.reset_alarm()
  90. _alarm.assert_called_with(0)
  91. def test_arm_alarm(self):
  92. if hasattr(signal, 'setitimer'):
  93. with patch('signal.setitimer', create=True) as seti:
  94. signals.arm_alarm(30)
  95. self.assertTrue(seti.called)
  96. def test_signum(self):
  97. self.assertEqual(signals.signum(13), 13)
  98. self.assertEqual(signals.signum('INT'), signal.SIGINT)
  99. self.assertEqual(signals.signum('SIGINT'), signal.SIGINT)
  100. with self.assertRaises(TypeError):
  101. signals.signum('int')
  102. signals.signum(object())
  103. @patch('signal.signal')
  104. def test_ignore(self, set):
  105. signals.ignore('SIGINT')
  106. set.assert_called_with(signals.signum('INT'), signals.ignored)
  107. signals.ignore('SIGTERM')
  108. set.assert_called_with(signals.signum('TERM'), signals.ignored)
  109. @patch('signal.signal')
  110. def test_setitem(self, set):
  111. handle = lambda *a: a
  112. signals['INT'] = handle
  113. set.assert_called_with(signal.SIGINT, handle)
  114. @patch('signal.signal')
  115. def test_setitem_raises(self, set):
  116. set.side_effect = ValueError()
  117. signals['INT'] = lambda *a: a
  118. if not platforms.IS_WINDOWS:
  119. class test_get_fdmax(Case):
  120. @patch('resource.getrlimit')
  121. def test_when_infinity(self, getrlimit):
  122. getrlimit.return_value = [None, resource.RLIM_INFINITY]
  123. default = object()
  124. self.assertIs(get_fdmax(default), default)
  125. @patch('resource.getrlimit')
  126. def test_when_actual(self, getrlimit):
  127. getrlimit.return_value = [None, 13]
  128. self.assertEqual(get_fdmax(None), 13)
  129. class test_maybe_drop_privileges(Case):
  130. @patch('celery.platforms.parse_uid')
  131. @patch('pwd.getpwuid')
  132. @patch('celery.platforms.setgid')
  133. @patch('celery.platforms.setuid')
  134. @patch('celery.platforms.initgroups')
  135. def test_with_uid(self, initgroups, setuid, setgid,
  136. getpwuid, parse_uid):
  137. class pw_struct(object):
  138. pw_gid = 50001
  139. def raise_on_second_call(*args, **kwargs):
  140. setuid.side_effect = OSError()
  141. setuid.side_effect.errno = errno.EPERM
  142. setuid.side_effect = raise_on_second_call
  143. getpwuid.return_value = pw_struct()
  144. parse_uid.return_value = 5001
  145. maybe_drop_privileges(uid='user')
  146. parse_uid.assert_called_with('user')
  147. getpwuid.assert_called_with(5001)
  148. setgid.assert_called_with(50001)
  149. initgroups.assert_called_with(5001, 50001)
  150. setuid.assert_has_calls([call(5001), call(0)])
  151. @patch('celery.platforms.parse_uid')
  152. @patch('celery.platforms.parse_gid')
  153. @patch('celery.platforms.setgid')
  154. @patch('celery.platforms.setuid')
  155. @patch('celery.platforms.initgroups')
  156. def test_with_guid(self, initgroups, setuid, setgid,
  157. parse_gid, parse_uid):
  158. def raise_on_second_call(*args, **kwargs):
  159. setuid.side_effect = OSError()
  160. setuid.side_effect.errno = errno.EPERM
  161. setuid.side_effect = raise_on_second_call
  162. parse_uid.return_value = 5001
  163. parse_gid.return_value = 50001
  164. maybe_drop_privileges(uid='user', gid='group')
  165. parse_uid.assert_called_with('user')
  166. parse_gid.assert_called_with('group')
  167. setgid.assert_called_with(50001)
  168. initgroups.assert_called_with(5001, 50001)
  169. setuid.assert_has_calls([call(5001), call(0)])
  170. setuid.side_effect = None
  171. with self.assertRaises(RuntimeError):
  172. maybe_drop_privileges(uid='user', gid='group')
  173. setuid.side_effect = OSError()
  174. setuid.side_effect.errno = errno.EINVAL
  175. with self.assertRaises(OSError):
  176. maybe_drop_privileges(uid='user', gid='group')
  177. @patch('celery.platforms.setuid')
  178. @patch('celery.platforms.setgid')
  179. @patch('celery.platforms.parse_gid')
  180. def test_only_gid(self, parse_gid, setgid, setuid):
  181. parse_gid.return_value = 50001
  182. maybe_drop_privileges(gid='group')
  183. parse_gid.assert_called_with('group')
  184. setgid.assert_called_with(50001)
  185. self.assertFalse(setuid.called)
  186. class test_setget_uid_gid(Case):
  187. @patch('celery.platforms.parse_uid')
  188. @patch('os.setuid')
  189. def test_setuid(self, _setuid, parse_uid):
  190. parse_uid.return_value = 5001
  191. setuid('user')
  192. parse_uid.assert_called_with('user')
  193. _setuid.assert_called_with(5001)
  194. @patch('celery.platforms.parse_gid')
  195. @patch('os.setgid')
  196. def test_setgid(self, _setgid, parse_gid):
  197. parse_gid.return_value = 50001
  198. setgid('group')
  199. parse_gid.assert_called_with('group')
  200. _setgid.assert_called_with(50001)
  201. def test_parse_uid_when_int(self):
  202. self.assertEqual(parse_uid(5001), 5001)
  203. @patch('pwd.getpwnam')
  204. def test_parse_uid_when_existing_name(self, getpwnam):
  205. class pwent(object):
  206. pw_uid = 5001
  207. getpwnam.return_value = pwent()
  208. self.assertEqual(parse_uid('user'), 5001)
  209. @patch('pwd.getpwnam')
  210. def test_parse_uid_when_nonexisting_name(self, getpwnam):
  211. getpwnam.side_effect = KeyError('user')
  212. with self.assertRaises(KeyError):
  213. parse_uid('user')
  214. def test_parse_gid_when_int(self):
  215. self.assertEqual(parse_gid(50001), 50001)
  216. @patch('grp.getgrnam')
  217. def test_parse_gid_when_existing_name(self, getgrnam):
  218. class grent(object):
  219. gr_gid = 50001
  220. getgrnam.return_value = grent()
  221. self.assertEqual(parse_gid('group'), 50001)
  222. @patch('grp.getgrnam')
  223. def test_parse_gid_when_nonexisting_name(self, getgrnam):
  224. getgrnam.side_effect = KeyError('group')
  225. with self.assertRaises(KeyError):
  226. parse_gid('group')
  227. class test_initgroups(Case):
  228. @patch('pwd.getpwuid')
  229. @patch('os.initgroups', create=True)
  230. def test_with_initgroups(self, initgroups_, getpwuid):
  231. getpwuid.return_value = ['user']
  232. initgroups(5001, 50001)
  233. initgroups_.assert_called_with('user', 50001)
  234. @patch('celery.platforms.setgroups')
  235. @patch('grp.getgrall')
  236. @patch('pwd.getpwuid')
  237. def test_without_initgroups(self, getpwuid, getgrall, setgroups):
  238. prev = getattr(os, 'initgroups', None)
  239. try:
  240. delattr(os, 'initgroups')
  241. except AttributeError:
  242. pass
  243. try:
  244. getpwuid.return_value = ['user']
  245. class grent(object):
  246. gr_mem = ['user']
  247. def __init__(self, gid):
  248. self.gr_gid = gid
  249. getgrall.return_value = [grent(1), grent(2), grent(3)]
  250. initgroups(5001, 50001)
  251. setgroups.assert_called_with([1, 2, 3])
  252. finally:
  253. if prev:
  254. os.initgroups = prev
  255. class test_detached(Case):
  256. def test_without_resource(self):
  257. prev, platforms.resource = platforms.resource, None
  258. try:
  259. with self.assertRaises(RuntimeError):
  260. detached()
  261. finally:
  262. platforms.resource = prev
  263. @patch('celery.platforms._create_pidlock')
  264. @patch('celery.platforms.signals')
  265. @patch('celery.platforms.maybe_drop_privileges')
  266. @patch('os.geteuid')
  267. @patch(open_fqdn)
  268. def test_default(self, open, geteuid, maybe_drop,
  269. signals, pidlock):
  270. geteuid.return_value = 0
  271. context = detached(uid='user', gid='group')
  272. self.assertIsInstance(context, DaemonContext)
  273. signals.reset.assert_called_with('SIGCLD')
  274. maybe_drop.assert_called_with(uid='user', gid='group')
  275. open.return_value = Mock()
  276. geteuid.return_value = 5001
  277. context = detached(uid='user', gid='group', logfile='/foo/bar')
  278. self.assertIsInstance(context, DaemonContext)
  279. self.assertTrue(context.after_chdir)
  280. context.after_chdir()
  281. open.assert_called_with('/foo/bar', 'a')
  282. open.return_value.close.assert_called_with()
  283. context = detached(pidfile='/foo/bar/pid')
  284. self.assertIsInstance(context, DaemonContext)
  285. self.assertTrue(context.after_chdir)
  286. context.after_chdir()
  287. pidlock.assert_called_with('/foo/bar/pid')
  288. class test_DaemonContext(Case):
  289. @patch('os.fork')
  290. @patch('os.setsid')
  291. @patch('os._exit')
  292. @patch('os.chdir')
  293. @patch('os.umask')
  294. @patch('os.close')
  295. @patch('os.closerange')
  296. @patch('os.open')
  297. @patch('os.dup2')
  298. def test_open(self, dup2, open, close, closer, umask, chdir,
  299. _exit, setsid, fork):
  300. x = DaemonContext(workdir='/opt/workdir')
  301. fork.return_value = 0
  302. with x:
  303. self.assertTrue(x._is_open)
  304. with x:
  305. pass
  306. self.assertEqual(fork.call_count, 2)
  307. setsid.assert_called_with()
  308. self.assertFalse(_exit.called)
  309. chdir.assert_called_with(x.workdir)
  310. umask.assert_called_with(x.umask)
  311. self.assertTrue(dup2.called)
  312. fork.reset_mock()
  313. fork.return_value = 1
  314. x = DaemonContext(workdir='/opt/workdir')
  315. with x:
  316. pass
  317. self.assertEqual(fork.call_count, 1)
  318. _exit.assert_called_with(0)
  319. x = DaemonContext(workdir='/opt/workdir', fake=True)
  320. x._detach = Mock()
  321. with x:
  322. pass
  323. self.assertFalse(x._detach.called)
  324. x.after_chdir = Mock()
  325. with x:
  326. pass
  327. x.after_chdir.assert_called_with()
  328. class test_Pidfile(Case):
  329. @patch('celery.platforms.Pidfile')
  330. def test_create_pidlock(self, Pidfile):
  331. p = Pidfile.return_value = Mock()
  332. p.is_locked.return_value = True
  333. p.remove_if_stale.return_value = False
  334. with self.assertRaises(SystemExit):
  335. create_pidlock('/var/pid')
  336. p.remove_if_stale.return_value = True
  337. ret = create_pidlock('/var/pid')
  338. self.assertIs(ret, p)
  339. def test_context(self):
  340. p = Pidfile('/var/pid')
  341. p.write_pid = Mock()
  342. p.remove = Mock()
  343. with p as _p:
  344. self.assertIs(_p, p)
  345. p.write_pid.assert_called_with()
  346. p.remove.assert_called_with()
  347. def test_acquire_raises_LockFailed(self):
  348. p = Pidfile('/var/pid')
  349. p.write_pid = Mock()
  350. p.write_pid.side_effect = OSError()
  351. with self.assertRaises(LockFailed):
  352. with p:
  353. pass
  354. @patch('os.path.exists')
  355. def test_is_locked(self, exists):
  356. p = Pidfile('/var/pid')
  357. exists.return_value = True
  358. self.assertTrue(p.is_locked())
  359. exists.return_value = False
  360. self.assertFalse(p.is_locked())
  361. def test_read_pid(self):
  362. with mock_open() as s:
  363. s.write('1816\n')
  364. s.seek(0)
  365. p = Pidfile('/var/pid')
  366. self.assertEqual(p.read_pid(), 1816)
  367. def test_read_pid_partially_written(self):
  368. with mock_open() as s:
  369. s.write('1816')
  370. s.seek(0)
  371. p = Pidfile('/var/pid')
  372. with self.assertRaises(ValueError):
  373. p.read_pid()
  374. def test_read_pid_raises_ENOENT(self):
  375. exc = IOError()
  376. exc.errno = errno.ENOENT
  377. with mock_open(side_effect=exc):
  378. p = Pidfile('/var/pid')
  379. self.assertIsNone(p.read_pid())
  380. def test_read_pid_raises_IOError(self):
  381. exc = IOError()
  382. exc.errno = errno.EAGAIN
  383. with mock_open(side_effect=exc):
  384. p = Pidfile('/var/pid')
  385. with self.assertRaises(IOError):
  386. p.read_pid()
  387. def test_read_pid_bogus_pidfile(self):
  388. with mock_open() as s:
  389. s.write('eighteensixteen\n')
  390. s.seek(0)
  391. p = Pidfile('/var/pid')
  392. with self.assertRaises(ValueError):
  393. p.read_pid()
  394. @patch('os.unlink')
  395. def test_remove(self, unlink):
  396. unlink.return_value = True
  397. p = Pidfile('/var/pid')
  398. p.remove()
  399. unlink.assert_called_with(p.path)
  400. @patch('os.unlink')
  401. def test_remove_ENOENT(self, unlink):
  402. exc = OSError()
  403. exc.errno = errno.ENOENT
  404. unlink.side_effect = exc
  405. p = Pidfile('/var/pid')
  406. p.remove()
  407. unlink.assert_called_with(p.path)
  408. @patch('os.unlink')
  409. def test_remove_EACCES(self, unlink):
  410. exc = OSError()
  411. exc.errno = errno.EACCES
  412. unlink.side_effect = exc
  413. p = Pidfile('/var/pid')
  414. p.remove()
  415. unlink.assert_called_with(p.path)
  416. @patch('os.unlink')
  417. def test_remove_OSError(self, unlink):
  418. exc = OSError()
  419. exc.errno = errno.EAGAIN
  420. unlink.side_effect = exc
  421. p = Pidfile('/var/pid')
  422. with self.assertRaises(OSError):
  423. p.remove()
  424. unlink.assert_called_with(p.path)
  425. @patch('os.kill')
  426. def test_remove_if_stale_process_alive(self, kill):
  427. p = Pidfile('/var/pid')
  428. p.read_pid = Mock()
  429. p.read_pid.return_value = 1816
  430. kill.return_value = 0
  431. self.assertFalse(p.remove_if_stale())
  432. kill.assert_called_with(1816, 0)
  433. p.read_pid.assert_called_with()
  434. kill.side_effect = OSError()
  435. kill.side_effect.errno = errno.ENOENT
  436. self.assertFalse(p.remove_if_stale())
  437. @patch('os.kill')
  438. def test_remove_if_stale_process_dead(self, kill):
  439. with override_stdouts():
  440. p = Pidfile('/var/pid')
  441. p.read_pid = Mock()
  442. p.read_pid.return_value = 1816
  443. p.remove = Mock()
  444. exc = OSError()
  445. exc.errno = errno.ESRCH
  446. kill.side_effect = exc
  447. self.assertTrue(p.remove_if_stale())
  448. kill.assert_called_with(1816, 0)
  449. p.remove.assert_called_with()
  450. def test_remove_if_stale_broken_pid(self):
  451. with override_stdouts():
  452. p = Pidfile('/var/pid')
  453. p.read_pid = Mock()
  454. p.read_pid.side_effect = ValueError()
  455. p.remove = Mock()
  456. self.assertTrue(p.remove_if_stale())
  457. p.remove.assert_called_with()
  458. def test_remove_if_stale_no_pidfile(self):
  459. p = Pidfile('/var/pid')
  460. p.read_pid = Mock()
  461. p.read_pid.return_value = None
  462. p.remove = Mock()
  463. self.assertTrue(p.remove_if_stale())
  464. p.remove.assert_called_with()
  465. @patch('os.fsync')
  466. @patch('os.getpid')
  467. @patch('os.open')
  468. @patch('os.fdopen')
  469. @patch(open_fqdn)
  470. def test_write_pid(self, open_, fdopen, osopen, getpid, fsync):
  471. getpid.return_value = 1816
  472. osopen.return_value = 13
  473. w = fdopen.return_value = WhateverIO()
  474. w.close = Mock()
  475. r = open_.return_value = WhateverIO()
  476. r.write('1816\n')
  477. r.seek(0)
  478. p = Pidfile('/var/pid')
  479. p.write_pid()
  480. w.seek(0)
  481. self.assertEqual(w.readline(), '1816\n')
  482. self.assertTrue(w.close.called)
  483. getpid.assert_called_with()
  484. osopen.assert_called_with(p.path, platforms.PIDFILE_FLAGS,
  485. platforms.PIDFILE_MODE)
  486. fdopen.assert_called_with(13, 'w')
  487. fsync.assert_called_with(13)
  488. open_.assert_called_with(p.path)
  489. @patch('os.fsync')
  490. @patch('os.getpid')
  491. @patch('os.open')
  492. @patch('os.fdopen')
  493. @patch(open_fqdn)
  494. def test_write_reread_fails(self, open_, fdopen,
  495. osopen, getpid, fsync):
  496. getpid.return_value = 1816
  497. osopen.return_value = 13
  498. w = fdopen.return_value = WhateverIO()
  499. w.close = Mock()
  500. r = open_.return_value = WhateverIO()
  501. r.write('11816\n')
  502. r.seek(0)
  503. p = Pidfile('/var/pid')
  504. with self.assertRaises(LockFailed):
  505. p.write_pid()
  506. class test_setgroups(Case):
  507. @patch('os.setgroups', create=True)
  508. def test_setgroups_hack_ValueError(self, setgroups):
  509. def on_setgroups(groups):
  510. if len(groups) <= 200:
  511. setgroups.return_value = True
  512. return
  513. raise ValueError()
  514. setgroups.side_effect = on_setgroups
  515. _setgroups_hack(list(range(400)))
  516. setgroups.side_effect = ValueError()
  517. with self.assertRaises(ValueError):
  518. _setgroups_hack(list(range(400)))
  519. @patch('os.setgroups', create=True)
  520. def test_setgroups_hack_OSError(self, setgroups):
  521. exc = OSError()
  522. exc.errno = errno.EINVAL
  523. def on_setgroups(groups):
  524. if len(groups) <= 200:
  525. setgroups.return_value = True
  526. return
  527. raise exc
  528. setgroups.side_effect = on_setgroups
  529. _setgroups_hack(list(range(400)))
  530. setgroups.side_effect = exc
  531. with self.assertRaises(OSError):
  532. _setgroups_hack(list(range(400)))
  533. exc2 = OSError()
  534. exc.errno = errno.ESRCH
  535. setgroups.side_effect = exc2
  536. with self.assertRaises(OSError):
  537. _setgroups_hack(list(range(400)))
  538. @patch('os.sysconf')
  539. @patch('celery.platforms._setgroups_hack')
  540. def test_setgroups(self, hack, sysconf):
  541. sysconf.return_value = 100
  542. setgroups(list(range(400)))
  543. hack.assert_called_with(list(range(100)))
  544. @patch('os.sysconf')
  545. @patch('celery.platforms._setgroups_hack')
  546. def test_setgroups_sysconf_raises(self, hack, sysconf):
  547. sysconf.side_effect = ValueError()
  548. setgroups(list(range(400)))
  549. hack.assert_called_with(list(range(400)))
  550. @patch('os.getgroups')
  551. @patch('os.sysconf')
  552. @patch('celery.platforms._setgroups_hack')
  553. def test_setgroups_raises_ESRCH(self, hack, sysconf, getgroups):
  554. sysconf.side_effect = ValueError()
  555. esrch = OSError()
  556. esrch.errno = errno.ESRCH
  557. hack.side_effect = esrch
  558. with self.assertRaises(OSError):
  559. setgroups(list(range(400)))
  560. @patch('os.getgroups')
  561. @patch('os.sysconf')
  562. @patch('celery.platforms._setgroups_hack')
  563. def test_setgroups_raises_EPERM(self, hack, sysconf, getgroups):
  564. sysconf.side_effect = ValueError()
  565. eperm = OSError()
  566. eperm.errno = errno.EPERM
  567. hack.side_effect = eperm
  568. getgroups.return_value = list(range(400))
  569. setgroups(list(range(400)))
  570. getgroups.assert_called_with()
  571. getgroups.return_value = [1000]
  572. with self.assertRaises(OSError):
  573. setgroups(list(range(400)))
  574. getgroups.assert_called_with()