Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.utils.process_unittest.py @ 8572f1fe

History | View | Annotate | Download (21 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Script for testing ganeti.utils.process"""
23

    
24
import unittest
25
import tempfile
26
import shutil
27
import os
28
import stat
29
import time
30
import signal
31

    
32
from ganeti import constants
33
from ganeti import utils
34
from ganeti import errors
35

    
36
import testutils
37

    
38

    
39
class TestIsProcessAlive(unittest.TestCase):
40
  """Testing case for IsProcessAlive"""
41

    
42
  def testExists(self):
43
    mypid = os.getpid()
44
    self.assert_(utils.IsProcessAlive(mypid), "can't find myself running")
45

    
46
  def testNotExisting(self):
47
    pid_non_existing = os.fork()
48
    if pid_non_existing == 0:
49
      os._exit(0)
50
    elif pid_non_existing < 0:
51
      raise SystemError("can't fork")
52
    os.waitpid(pid_non_existing, 0)
53
    self.assertFalse(utils.IsProcessAlive(pid_non_existing),
54
                     "nonexisting process detected")
55

    
56

    
57
class TestGetProcStatusPath(unittest.TestCase):
58
  def test(self):
59
    self.assert_("/1234/" in utils.process._GetProcStatusPath(1234))
60
    self.assertNotEqual(utils.process._GetProcStatusPath(1),
61
                        utils.process._GetProcStatusPath(2))
62

    
63

    
64
class TestIsProcessHandlingSignal(unittest.TestCase):
65
  def setUp(self):
66
    self.tmpdir = tempfile.mkdtemp()
67

    
68
  def tearDown(self):
69
    shutil.rmtree(self.tmpdir)
70

    
71
  def testParseSigsetT(self):
72
    parse_sigset_t_fn = utils.process._ParseSigsetT
73
    self.assertEqual(len(parse_sigset_t_fn("0")), 0)
74
    self.assertEqual(parse_sigset_t_fn("1"), set([1]))
75
    self.assertEqual(parse_sigset_t_fn("1000a"), set([2, 4, 17]))
76
    self.assertEqual(parse_sigset_t_fn("810002"), set([2, 17, 24, ]))
77
    self.assertEqual(parse_sigset_t_fn("0000000180000202"),
78
                     set([2, 10, 32, 33]))
79
    self.assertEqual(parse_sigset_t_fn("0000000180000002"),
80
                     set([2, 32, 33]))
81
    self.assertEqual(parse_sigset_t_fn("0000000188000002"),
82
                     set([2, 28, 32, 33]))
83
    self.assertEqual(parse_sigset_t_fn("000000004b813efb"),
84
                     set([1, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 17,
85
                          24, 25, 26, 28, 31]))
86
    self.assertEqual(parse_sigset_t_fn("ffffff"), set(range(1, 25)))
87

    
88
  def testGetProcStatusField(self):
89
    for field in ["SigCgt", "Name", "FDSize"]:
90
      for value in ["", "0", "cat", "  1234 KB"]:
91
        pstatus = "\n".join([
92
          "VmPeak: 999 kB",
93
          "%s: %s" % (field, value),
94
          "TracerPid: 0",
95
          ])
96
        result = utils.process._GetProcStatusField(pstatus, field)
97
        self.assertEqual(result, value.strip())
98

    
99
  def test(self):
100
    sp = utils.PathJoin(self.tmpdir, "status")
101

    
102
    utils.WriteFile(sp, data="\n".join([
103
      "Name:   bash",
104
      "State:  S (sleeping)",
105
      "SleepAVG:       98%",
106
      "Pid:    22250",
107
      "PPid:   10858",
108
      "TracerPid:      0",
109
      "SigBlk: 0000000000010000",
110
      "SigIgn: 0000000000384004",
111
      "SigCgt: 000000004b813efb",
112
      "CapEff: 0000000000000000",
113
      ]))
114

    
115
    self.assert_(utils.IsProcessHandlingSignal(1234, 10, status_path=sp))
116

    
117
  def testNoSigCgt(self):
118
    sp = utils.PathJoin(self.tmpdir, "status")
119

    
120
    utils.WriteFile(sp, data="\n".join([
121
      "Name:   bash",
122
      ]))
123

    
124
    self.assertRaises(RuntimeError, utils.IsProcessHandlingSignal,
125
                      1234, 10, status_path=sp)
126

    
127
  def testNoSuchFile(self):
128
    sp = utils.PathJoin(self.tmpdir, "notexist")
129

    
130
    self.assertFalse(utils.IsProcessHandlingSignal(1234, 10, status_path=sp))
131

    
132
  @staticmethod
133
  def _TestRealProcess():
134
    signal.signal(signal.SIGUSR1, signal.SIG_DFL)
135
    if utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1):
136
      raise Exception("SIGUSR1 is handled when it should not be")
137

    
138
    signal.signal(signal.SIGUSR1, lambda signum, frame: None)
139
    if not utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1):
140
      raise Exception("SIGUSR1 is not handled when it should be")
141

    
142
    signal.signal(signal.SIGUSR1, signal.SIG_IGN)
143
    if utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1):
144
      raise Exception("SIGUSR1 is not handled when it should be")
145

    
146
    signal.signal(signal.SIGUSR1, signal.SIG_DFL)
147
    if utils.IsProcessHandlingSignal(os.getpid(), signal.SIGUSR1):
148
      raise Exception("SIGUSR1 is handled when it should not be")
149

    
150
    return True
151

    
152
  def testRealProcess(self):
153
    self.assert_(utils.RunInSeparateProcess(self._TestRealProcess))
154

    
155

    
156
class TestRunCmd(testutils.GanetiTestCase):
157
  """Testing case for the RunCmd function"""
158

    
159
  def setUp(self):
160
    testutils.GanetiTestCase.setUp(self)
161
    self.magic = time.ctime() + " ganeti test"
162
    self.fname = self._CreateTempFile()
163
    self.fifo_tmpdir = tempfile.mkdtemp()
164
    self.fifo_file = os.path.join(self.fifo_tmpdir, "ganeti_test_fifo")
165
    os.mkfifo(self.fifo_file)
166

    
167
  def tearDown(self):
168
    shutil.rmtree(self.fifo_tmpdir)
169
    testutils.GanetiTestCase.tearDown(self)
170

    
171
  def testOk(self):
172
    """Test successful exit code"""
173
    result = utils.RunCmd("/bin/sh -c 'exit 0'")
174
    self.assertEqual(result.exit_code, 0)
175
    self.assertEqual(result.output, "")
176

    
177
  def testFail(self):
178
    """Test fail exit code"""
179
    result = utils.RunCmd("/bin/sh -c 'exit 1'")
180
    self.assertEqual(result.exit_code, 1)
181
    self.assertEqual(result.output, "")
182

    
183
  def testStdout(self):
184
    """Test standard output"""
185
    cmd = 'echo -n "%s"' % self.magic
186
    result = utils.RunCmd("/bin/sh -c '%s'" % cmd)
187
    self.assertEqual(result.stdout, self.magic)
188
    result = utils.RunCmd("/bin/sh -c '%s'" % cmd, output=self.fname)
189
    self.assertEqual(result.output, "")
190
    self.assertFileContent(self.fname, self.magic)
191

    
192
  def testStderr(self):
193
    """Test standard error"""
194
    cmd = 'echo -n "%s"' % self.magic
195
    result = utils.RunCmd("/bin/sh -c '%s' 1>&2" % cmd)
196
    self.assertEqual(result.stderr, self.magic)
197
    result = utils.RunCmd("/bin/sh -c '%s' 1>&2" % cmd, output=self.fname)
198
    self.assertEqual(result.output, "")
199
    self.assertFileContent(self.fname, self.magic)
200

    
201
  def testCombined(self):
202
    """Test combined output"""
203
    cmd = 'echo -n "A%s"; echo -n "B%s" 1>&2' % (self.magic, self.magic)
204
    expected = "A" + self.magic + "B" + self.magic
205
    result = utils.RunCmd("/bin/sh -c '%s'" % cmd)
206
    self.assertEqual(result.output, expected)
207
    result = utils.RunCmd("/bin/sh -c '%s'" % cmd, output=self.fname)
208
    self.assertEqual(result.output, "")
209
    self.assertFileContent(self.fname, expected)
210

    
211
  def testSignal(self):
212
    """Test signal"""
213
    result = utils.RunCmd(["python", "-c",
214
                           "import os; os.kill(os.getpid(), 15)"])
215
    self.assertEqual(result.signal, 15)
216
    self.assertEqual(result.output, "")
217

    
218
  def testTimeoutClean(self):
219
    cmd = "trap 'exit 0' TERM; read < %s" % self.fifo_file
220
    result = utils.RunCmd(["/bin/sh", "-c", cmd], timeout=0.2)
221
    self.assertEqual(result.exit_code, 0)
222

    
223
  def testTimeoutKill(self):
224
    cmd = ["/bin/sh", "-c", "trap '' TERM; read < %s" % self.fifo_file]
225
    timeout = 0.2
226
    (out, err, status, ta) = \
227
      utils.process._RunCmdPipe(cmd, {}, False, "/", False,
228
                                timeout, None, _linger_timeout=0.2)
229
    self.assert_(status < 0)
230
    self.assertEqual(-status, signal.SIGKILL)
231

    
232
  def testTimeoutOutputAfterTerm(self):
233
    cmd = "trap 'echo sigtermed; exit 1' TERM; read < %s" % self.fifo_file
234
    result = utils.RunCmd(["/bin/sh", "-c", cmd], timeout=0.2)
235
    self.assert_(result.failed)
236
    self.assertEqual(result.stdout, "sigtermed\n")
237

    
238
  def testListRun(self):
239
    """Test list runs"""
240
    result = utils.RunCmd(["true"])
241
    self.assertEqual(result.signal, None)
242
    self.assertEqual(result.exit_code, 0)
243
    result = utils.RunCmd(["/bin/sh", "-c", "exit 1"])
244
    self.assertEqual(result.signal, None)
245
    self.assertEqual(result.exit_code, 1)
246
    result = utils.RunCmd(["echo", "-n", self.magic])
247
    self.assertEqual(result.signal, None)
248
    self.assertEqual(result.exit_code, 0)
249
    self.assertEqual(result.stdout, self.magic)
250

    
251
  def testFileEmptyOutput(self):
252
    """Test file output"""
253
    result = utils.RunCmd(["true"], output=self.fname)
254
    self.assertEqual(result.signal, None)
255
    self.assertEqual(result.exit_code, 0)
256
    self.assertFileContent(self.fname, "")
257

    
258
  def testLang(self):
259
    """Test locale environment"""
260
    old_env = os.environ.copy()
261
    try:
262
      os.environ["LANG"] = "en_US.UTF-8"
263
      os.environ["LC_ALL"] = "en_US.UTF-8"
264
      result = utils.RunCmd(["locale"])
265
      for line in result.output.splitlines():
266
        key, value = line.split("=", 1)
267
        # Ignore these variables, they're overridden by LC_ALL
268
        if key == "LANG" or key == "LANGUAGE":
269
          continue
270
        self.failIf(value and value != "C" and value != '"C"',
271
            "Variable %s is set to the invalid value '%s'" % (key, value))
272
    finally:
273
      os.environ = old_env
274

    
275
  def testDefaultCwd(self):
276
    """Test default working directory"""
277
    self.failUnlessEqual(utils.RunCmd(["pwd"]).stdout.strip(), "/")
278

    
279
  def testCwd(self):
280
    """Test default working directory"""
281
    self.failUnlessEqual(utils.RunCmd(["pwd"], cwd="/").stdout.strip(), "/")
282
    self.failUnlessEqual(utils.RunCmd(["pwd"], cwd="/tmp").stdout.strip(),
283
                         "/tmp")
284
    cwd = os.getcwd()
285
    self.failUnlessEqual(utils.RunCmd(["pwd"], cwd=cwd).stdout.strip(), cwd)
286

    
287
  def testResetEnv(self):
288
    """Test environment reset functionality"""
289
    self.failUnlessEqual(utils.RunCmd(["env"], reset_env=True).stdout.strip(),
290
                         "")
291
    self.failUnlessEqual(utils.RunCmd(["env"], reset_env=True,
292
                                      env={"FOO": "bar",}).stdout.strip(),
293
                         "FOO=bar")
294

    
295
  def testNoFork(self):
296
    """Test that nofork raise an error"""
297
    self.assertFalse(utils.process._no_fork)
298
    utils.DisableFork()
299
    try:
300
      self.assertTrue(utils.process._no_fork)
301
      self.assertRaises(errors.ProgrammerError, utils.RunCmd, ["true"])
302
    finally:
303
      utils.process._no_fork = False
304
    self.assertFalse(utils.process._no_fork)
305

    
306
  def testWrongParams(self):
307
    """Test wrong parameters"""
308
    self.assertRaises(errors.ProgrammerError, utils.RunCmd, ["true"],
309
                      output="/dev/null", interactive=True)
310

    
311
  def testNocloseFds(self):
312
    """Test selective fd retention (noclose_fds)"""
313
    temp = open(self.fname, "r+")
314
    try:
315
      temp.write("test")
316
      temp.seek(0)
317
      cmd = "read -u %d; echo $REPLY" % temp.fileno()
318
      result = utils.RunCmd(["/bin/bash", "-c", cmd])
319
      self.assertEqual(result.stdout.strip(), "")
320
      temp.seek(0)
321
      result = utils.RunCmd(["/bin/bash", "-c", cmd],
322
                            noclose_fds=[temp.fileno()])
323
      self.assertEqual(result.stdout.strip(), "test")
324
    finally:
325
      temp.close()
326

    
327

    
328
class TestRunParts(testutils.GanetiTestCase):
329
  """Testing case for the RunParts function"""
330

    
331
  def setUp(self):
332
    self.rundir = tempfile.mkdtemp(prefix="ganeti-test", suffix=".tmp")
333

    
334
  def tearDown(self):
335
    shutil.rmtree(self.rundir)
336

    
337
  def testEmpty(self):
338
    """Test on an empty dir"""
339
    self.failUnlessEqual(utils.RunParts(self.rundir, reset_env=True), [])
340

    
341
  def testSkipWrongName(self):
342
    """Test that wrong files are skipped"""
343
    fname = os.path.join(self.rundir, "00test.dot")
344
    utils.WriteFile(fname, data="")
345
    os.chmod(fname, stat.S_IREAD | stat.S_IEXEC)
346
    relname = os.path.basename(fname)
347
    self.failUnlessEqual(utils.RunParts(self.rundir, reset_env=True),
348
                         [(relname, constants.RUNPARTS_SKIP, None)])
349

    
350
  def testSkipNonExec(self):
351
    """Test that non executable files are skipped"""
352
    fname = os.path.join(self.rundir, "00test")
353
    utils.WriteFile(fname, data="")
354
    relname = os.path.basename(fname)
355
    self.failUnlessEqual(utils.RunParts(self.rundir, reset_env=True),
356
                         [(relname, constants.RUNPARTS_SKIP, None)])
357

    
358
  def testError(self):
359
    """Test error on a broken executable"""
360
    fname = os.path.join(self.rundir, "00test")
361
    utils.WriteFile(fname, data="")
362
    os.chmod(fname, stat.S_IREAD | stat.S_IEXEC)
363
    (relname, status, error) = utils.RunParts(self.rundir, reset_env=True)[0]
364
    self.failUnlessEqual(relname, os.path.basename(fname))
365
    self.failUnlessEqual(status, constants.RUNPARTS_ERR)
366
    self.failUnless(error)
367

    
368
  def testSorted(self):
369
    """Test executions are sorted"""
370
    files = []
371
    files.append(os.path.join(self.rundir, "64test"))
372
    files.append(os.path.join(self.rundir, "00test"))
373
    files.append(os.path.join(self.rundir, "42test"))
374

    
375
    for fname in files:
376
      utils.WriteFile(fname, data="")
377

    
378
    results = utils.RunParts(self.rundir, reset_env=True)
379

    
380
    for fname in sorted(files):
381
      self.failUnlessEqual(os.path.basename(fname), results.pop(0)[0])
382

    
383
  def testOk(self):
384
    """Test correct execution"""
385
    fname = os.path.join(self.rundir, "00test")
386
    utils.WriteFile(fname, data="#!/bin/sh\n\necho -n ciao")
387
    os.chmod(fname, stat.S_IREAD | stat.S_IEXEC)
388
    (relname, status, runresult) = \
389
      utils.RunParts(self.rundir, reset_env=True)[0]
390
    self.failUnlessEqual(relname, os.path.basename(fname))
391
    self.failUnlessEqual(status, constants.RUNPARTS_RUN)
392
    self.failUnlessEqual(runresult.stdout, "ciao")
393

    
394
  def testRunFail(self):
395
    """Test correct execution, with run failure"""
396
    fname = os.path.join(self.rundir, "00test")
397
    utils.WriteFile(fname, data="#!/bin/sh\n\nexit 1")
398
    os.chmod(fname, stat.S_IREAD | stat.S_IEXEC)
399
    (relname, status, runresult) = \
400
      utils.RunParts(self.rundir, reset_env=True)[0]
401
    self.failUnlessEqual(relname, os.path.basename(fname))
402
    self.failUnlessEqual(status, constants.RUNPARTS_RUN)
403
    self.failUnlessEqual(runresult.exit_code, 1)
404
    self.failUnless(runresult.failed)
405

    
406
  def testRunMix(self):
407
    files = []
408
    files.append(os.path.join(self.rundir, "00test"))
409
    files.append(os.path.join(self.rundir, "42test"))
410
    files.append(os.path.join(self.rundir, "64test"))
411
    files.append(os.path.join(self.rundir, "99test"))
412

    
413
    files.sort()
414

    
415
    # 1st has errors in execution
416
    utils.WriteFile(files[0], data="#!/bin/sh\n\nexit 1")
417
    os.chmod(files[0], stat.S_IREAD | stat.S_IEXEC)
418

    
419
    # 2nd is skipped
420
    utils.WriteFile(files[1], data="")
421

    
422
    # 3rd cannot execute properly
423
    utils.WriteFile(files[2], data="")
424
    os.chmod(files[2], stat.S_IREAD | stat.S_IEXEC)
425

    
426
    # 4th execs
427
    utils.WriteFile(files[3], data="#!/bin/sh\n\necho -n ciao")
428
    os.chmod(files[3], stat.S_IREAD | stat.S_IEXEC)
429

    
430
    results = utils.RunParts(self.rundir, reset_env=True)
431

    
432
    (relname, status, runresult) = results[0]
433
    self.failUnlessEqual(relname, os.path.basename(files[0]))
434
    self.failUnlessEqual(status, constants.RUNPARTS_RUN)
435
    self.failUnlessEqual(runresult.exit_code, 1)
436
    self.failUnless(runresult.failed)
437

    
438
    (relname, status, runresult) = results[1]
439
    self.failUnlessEqual(relname, os.path.basename(files[1]))
440
    self.failUnlessEqual(status, constants.RUNPARTS_SKIP)
441
    self.failUnlessEqual(runresult, None)
442

    
443
    (relname, status, runresult) = results[2]
444
    self.failUnlessEqual(relname, os.path.basename(files[2]))
445
    self.failUnlessEqual(status, constants.RUNPARTS_ERR)
446
    self.failUnless(runresult)
447

    
448
    (relname, status, runresult) = results[3]
449
    self.failUnlessEqual(relname, os.path.basename(files[3]))
450
    self.failUnlessEqual(status, constants.RUNPARTS_RUN)
451
    self.failUnlessEqual(runresult.output, "ciao")
452
    self.failUnlessEqual(runresult.exit_code, 0)
453
    self.failUnless(not runresult.failed)
454

    
455
  def testMissingDirectory(self):
456
    nosuchdir = utils.PathJoin(self.rundir, "no/such/directory")
457
    self.assertEqual(utils.RunParts(nosuchdir), [])
458

    
459

    
460
class TestStartDaemon(testutils.GanetiTestCase):
461
  def setUp(self):
462
    self.tmpdir = tempfile.mkdtemp(prefix="ganeti-test")
463
    self.tmpfile = os.path.join(self.tmpdir, "test")
464

    
465
  def tearDown(self):
466
    shutil.rmtree(self.tmpdir)
467

    
468
  def testShell(self):
469
    utils.StartDaemon("echo Hello World > %s" % self.tmpfile)
470
    self._wait(self.tmpfile, 60.0, "Hello World")
471

    
472
  def testShellOutput(self):
473
    utils.StartDaemon("echo Hello World", output=self.tmpfile)
474
    self._wait(self.tmpfile, 60.0, "Hello World")
475

    
476
  def testNoShellNoOutput(self):
477
    utils.StartDaemon(["pwd"])
478

    
479
  def testNoShellNoOutputTouch(self):
480
    testfile = os.path.join(self.tmpdir, "check")
481
    self.failIf(os.path.exists(testfile))
482
    utils.StartDaemon(["touch", testfile])
483
    self._wait(testfile, 60.0, "")
484

    
485
  def testNoShellOutput(self):
486
    utils.StartDaemon(["pwd"], output=self.tmpfile)
487
    self._wait(self.tmpfile, 60.0, "/")
488

    
489
  def testNoShellOutputCwd(self):
490
    utils.StartDaemon(["pwd"], output=self.tmpfile, cwd=os.getcwd())
491
    self._wait(self.tmpfile, 60.0, os.getcwd())
492

    
493
  def testShellEnv(self):
494
    utils.StartDaemon("echo \"$GNT_TEST_VAR\"", output=self.tmpfile,
495
                      env={ "GNT_TEST_VAR": "Hello World", })
496
    self._wait(self.tmpfile, 60.0, "Hello World")
497

    
498
  def testNoShellEnv(self):
499
    utils.StartDaemon(["printenv", "GNT_TEST_VAR"], output=self.tmpfile,
500
                      env={ "GNT_TEST_VAR": "Hello World", })
501
    self._wait(self.tmpfile, 60.0, "Hello World")
502

    
503
  def testOutputFd(self):
504
    fd = os.open(self.tmpfile, os.O_WRONLY | os.O_CREAT)
505
    try:
506
      utils.StartDaemon(["pwd"], output_fd=fd, cwd=os.getcwd())
507
    finally:
508
      os.close(fd)
509
    self._wait(self.tmpfile, 60.0, os.getcwd())
510

    
511
  def testPid(self):
512
    pid = utils.StartDaemon("echo $$ > %s" % self.tmpfile)
513
    self._wait(self.tmpfile, 60.0, str(pid))
514

    
515
  def testPidFile(self):
516
    pidfile = os.path.join(self.tmpdir, "pid")
517
    checkfile = os.path.join(self.tmpdir, "abort")
518

    
519
    pid = utils.StartDaemon("while sleep 5; do :; done", pidfile=pidfile,
520
                            output=self.tmpfile)
521
    try:
522
      fd = os.open(pidfile, os.O_RDONLY)
523
      try:
524
        # Check file is locked
525
        self.assertRaises(errors.LockError, utils.LockFile, fd)
526

    
527
        pidtext = os.read(fd, 100)
528
      finally:
529
        os.close(fd)
530

    
531
      self.assertEqual(int(pidtext.strip()), pid)
532

    
533
      self.assert_(utils.IsProcessAlive(pid))
534
    finally:
535
      # No matter what happens, kill daemon
536
      utils.KillProcess(pid, timeout=5.0, waitpid=False)
537
      self.failIf(utils.IsProcessAlive(pid))
538

    
539
    self.assertEqual(utils.ReadFile(self.tmpfile), "")
540

    
541
  def _wait(self, path, timeout, expected):
542
    # Due to the asynchronous nature of daemon processes, polling is necessary.
543
    # A timeout makes sure the test doesn't hang forever.
544
    def _CheckFile():
545
      if not (os.path.isfile(path) and
546
              utils.ReadFile(path).strip() == expected):
547
        raise utils.RetryAgain()
548

    
549
    try:
550
      utils.Retry(_CheckFile, (0.01, 1.5, 1.0), timeout)
551
    except utils.RetryTimeout:
552
      self.fail("Apparently the daemon didn't run in %s seconds and/or"
553
                " didn't write the correct output" % timeout)
554

    
555
  def testError(self):
556
    self.assertRaises(errors.OpExecError, utils.StartDaemon,
557
                      ["./does-NOT-EXIST/here/0123456789"])
558
    self.assertRaises(errors.OpExecError, utils.StartDaemon,
559
                      ["./does-NOT-EXIST/here/0123456789"],
560
                      output=os.path.join(self.tmpdir, "DIR/NOT/EXIST"))
561
    self.assertRaises(errors.OpExecError, utils.StartDaemon,
562
                      ["./does-NOT-EXIST/here/0123456789"],
563
                      cwd=os.path.join(self.tmpdir, "DIR/NOT/EXIST"))
564
    self.assertRaises(errors.OpExecError, utils.StartDaemon,
565
                      ["./does-NOT-EXIST/here/0123456789"],
566
                      output=os.path.join(self.tmpdir, "DIR/NOT/EXIST"))
567

    
568
    fd = os.open(self.tmpfile, os.O_WRONLY | os.O_CREAT)
569
    try:
570
      self.assertRaises(errors.ProgrammerError, utils.StartDaemon,
571
                        ["./does-NOT-EXIST/here/0123456789"],
572
                        output=self.tmpfile, output_fd=fd)
573
    finally:
574
      os.close(fd)
575

    
576

    
577
class RunInSeparateProcess(unittest.TestCase):
578
  def test(self):
579
    for exp in [True, False]:
580
      def _child():
581
        return exp
582

    
583
      self.assertEqual(exp, utils.RunInSeparateProcess(_child))
584

    
585
  def testArgs(self):
586
    for arg in [0, 1, 999, "Hello World", (1, 2, 3)]:
587
      def _child(carg1, carg2):
588
        return carg1 == "Foo" and carg2 == arg
589

    
590
      self.assert_(utils.RunInSeparateProcess(_child, "Foo", arg))
591

    
592
  def testPid(self):
593
    parent_pid = os.getpid()
594

    
595
    def _check():
596
      return os.getpid() == parent_pid
597

    
598
    self.failIf(utils.RunInSeparateProcess(_check))
599

    
600
  def testSignal(self):
601
    def _kill():
602
      os.kill(os.getpid(), signal.SIGTERM)
603

    
604
    self.assertRaises(errors.GenericError,
605
                      utils.RunInSeparateProcess, _kill)
606

    
607
  def testException(self):
608
    def _exc():
609
      raise errors.GenericError("This is a test")
610

    
611
    self.assertRaises(errors.GenericError,
612
                      utils.RunInSeparateProcess, _exc)
613

    
614

    
615
if __name__ == "__main__":
616
  testutils.GanetiTestProgram()