[Buildbot-commits] buildbot/buildbot/test sleep.py,NONE,1.1 test_slavecommand.py,1.14,1.15

Brian Warner warner at users.sourceforge.net
Tue Jul 19 01:55:23 UTC 2005


Update of /cvsroot/buildbot/buildbot/buildbot/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10106/buildbot/test

Modified Files:
	test_slavecommand.py 
Added Files:
	sleep.py 
Log Message:
Revision: arch at buildbot.sf.net--2004/buildbot--dev--0--patch-234
Creator:  Brian Warner <warner at monolith.lothar.com>

overhaul ShellCommand timeout/interrupt/cleanup, add tests

	* buildbot/slave/commands.py (ShellCommand): overhaul
	error-handling code, to try and make timeout/interrupt work
	properly, and make win32 happier
	* buildbot/test/test_slavecommand.py: clean up, stop using
	reactor.iterate, add tests for timeout and interrupt
	* buildbot/test/sleep.py: utility for a new timeout test

	* buildbot/twcompat.py: copy over twisted 1.3/2.0 compatibility
	code from the local-usebranches branch


--- NEW FILE: sleep.py ---
#! /usr/bin/python

import sys, time
delay = int(sys.argv[1])

sys.stdout.write("sleeping for %d seconds\n" % delay)
time.sleep(delay)
sys.stdout.write("woke up\n")
sys.exit(0)

Index: test_slavecommand.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_slavecommand.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- test_slavecommand.py	16 May 2005 08:50:23 -0000	1.14
+++ test_slavecommand.py	19 Jul 2005 01:55:20 -0000	1.15
@@ -1,8 +1,9 @@
 # -*- test-case-name: buildbot.test.test_slavecommand -*-
 
 from twisted.trial import unittest
-from twisted.internet import reactor, defer
-from twisted.python import util, runtime
+from twisted.internet import reactor
+from twisted.python import util, runtime, failure
+from buildbot.twcompat import maybeWait
 
 noisy = False
 if noisy:
@@ -10,10 +11,11 @@
     import sys
     startLogging(sys.stdout)
 
-import os, re, time, sys
+import os, re, sys
 import signal
 
-from buildbot.slave.commands import SlaveShellCommand
+from buildbot.slave import commands
+SlaveShellCommand = commands.SlaveShellCommand
 
 # test slavecommand.py by running the various commands with a fake
 # SlaveBuilder object that logs the calls to sendUpdate()
@@ -21,28 +23,13 @@
 def findDir():
     # the same directory that holds this script
     return util.sibpath(__file__, ".")
-
-class FakeSlaveBuild:
-    pass
         
 class FakeSlaveBuilder:
-    def __init__(self, d, usePTY):
+    def __init__(self, usePTY):
         self.updates = []
-        self.failure = None
-        self.deferred = d
         self.basedir = findDir()
         self.usePTY = usePTY
 
-    def startBuild(self):
-        self.build = FakeSlaveBuild()
-    def commandComplete(self, dummy):
-        if noisy: print "FakeSlaveBuilder.commandComplete"
-        self.completed = 1
-        self.deferred.callback(0)
-    def commandFailed(self, failure):
-        if noisy: print "FakeSlaveBuilder.commandFailed", failure
-        self.failure = failure
-        self.deferred.callback(1)
     def sendUpdate(self, data):
         if noisy: print "FakeSlaveBuilder.sendUpdate", data
         self.updates.append(data)
@@ -64,40 +51,14 @@
             signal.signal(signal.SIGCHLD, self.sigchldHandler)
 
 
-class Shell(SignalMixin, unittest.TestCase):
-    usePTY = False
+class ShellBase(SignalMixin):
 
     def setUp(self):
-        d = defer.Deferred()
-        self.builder = FakeSlaveBuilder(d, self.usePTY)
-        d.addCallback(self.callback)
-        self.failed = None
-        self.results = None
-
-    def callback(self, failed):
-        self.failed = failed
-        self.results = self.builder.updates
-
-    def doTest(self, commandfactory, args):
-        builder = self.builder
-        builder.startBuild()
-        stepId = None
-        cmd = commandfactory(builder, stepId, args)
-        d = cmd.start()
-        d.addCallbacks(builder.commandComplete, builder.commandFailed)
-
-        timeout = time.time() + 2
-        while not (self.results or self.failed) and time.time() < timeout:
-            reactor.iterate(0.01)
-        if not (self.results or self.failed):
-            self.fail("timeout")
-        if self.failed:
-            print self.builder.failure
-        return self.failed
+        self.builder = FakeSlaveBuilder(self.usePTY)
 
     def getfile(self, which):
         got = ""
-        for r in self.results:
+        for r in self.builder.updates:
             if r.has_key(which):
                 got += r[which]
         return got
@@ -126,8 +87,8 @@
             self.assertEquals(got, contents)
 
     def getrc(self):
-        self.failUnless(self.results[-1].has_key('rc'))
-        got = self.results[-1]['rc']
+        self.failUnless(self.builder.updates[-1].has_key('rc'))
+        got = self.builder.updates[-1]['rc']
         return got
     def checkrc(self, expected):
         got = self.getrc()
@@ -136,48 +97,69 @@
     def testShell1(self):
         cmd = sys.executable + " emit.py 0"
         args = {'command': cmd, 'workdir': '.', 'timeout': 60}
-        failed = self.doTest(SlaveShellCommand, args)
-        self.failIf(failed)
-        self.checkOutput([('stdout', "this is stdout\n"),
-                          ('stderr', "this is stderr\n")])
-        self.checkrc(0)
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        expected = [('stdout', "this is stdout\n"),
+                    ('stderr', "this is stderr\n")]
+        d.addCallback(self._checkPass, expected, 0)
+        return maybeWait(d)
+
+    def _checkPass(self, res, expected, rc):
+        self.checkOutput(expected)
+        self.checkrc(rc)
 
     def testShell2(self):
-        cmd = sys.executable + " emit.py 1"
+        cmd = [sys.executable, "emit.py", "0"]
         args = {'command': cmd, 'workdir': '.', 'timeout': 60}
-        failed = self.doTest(SlaveShellCommand, args)
-        self.failIf(failed)
-        self.checkOutput([('stdout', "this is stdout\n"),
-                          ('stderr', "this is stderr\n")])
-        self.checkrc(1)
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        expected = [('stdout', "this is stdout\n"),
+                    ('stderr', "this is stderr\n")]
+        d.addCallback(self._checkPass, expected, 0)
+        return maybeWait(d)
 
-    def testShell3(self):
+    def testShellRC(self):
+        cmd = [sys.executable, "emit.py", "1"]
+        args = {'command': cmd, 'workdir': '.', 'timeout': 60}
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        expected = [('stdout', "this is stdout\n"),
+                    ('stderr', "this is stderr\n")]
+        d.addCallback(self._checkPass, expected, 1)
+        return maybeWait(d)
+
+    def testShellEnv(self):
         cmd = sys.executable + " emit.py 0"
         args = {'command': cmd, 'workdir': '.',
                 'env': {'EMIT_TEST': "envtest"}, 'timeout': 60}
-        failed = self.doTest(SlaveShellCommand, args)
-        self.failIf(failed)
-        self.checkOutput([('stdout', "this is stdout\n"),
-                          ('stderr', "this is stderr\n"),
-                          ('stdout', "EMIT_TEST: envtest\n"),
-                          ])
-        self.checkrc(0)
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        expected = [('stdout', "this is stdout\n"),
+                    ('stderr', "this is stderr\n"),
+                    ('stdout', "EMIT_TEST: envtest\n"),
+                    ]
+        d.addCallback(self._checkPass, expected, 0)
+        return maybeWait(d)
 
-    def testShell4(self):
+    def testShellSubdir(self):
         cmd = sys.executable + " emit.py 0"
         args = {'command': cmd, 'workdir': "subdir", 'timeout': 60}
-        failed = self.doTest(SlaveShellCommand, args)
-        self.failIf(failed)
-        self.checkOutput([('stdout', "this is stdout in subdir\n"),
-                          ('stderr', "this is stderr\n")])
-        self.checkrc(0)
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        expected = [('stdout', "this is stdout in subdir\n"),
+                    ('stderr', "this is stderr\n")]
+        d.addCallback(self._checkPass, expected, 0)
+        return maybeWait(d)
 
-    def testShellZ(self):
+    def testShellMissingCommand(self):
         args = {'command': "/bin/EndWorldHungerAndMakePigsFly",
                 'workdir': '.', 'timeout': 10}
-        failed = self.doTest(SlaveShellCommand, args)
-        self.failIf(failed)
-        self.failUnless(self.getrc() != 0)
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        d.addCallback(self._testShellMissingCommand_1)
+        return maybeWait(d)
+    def _testShellMissingCommand_1(self, res):
+        self.failIfEqual(self.getrc(), 0)
         got = self.getfile('stdout') + self.getfile('stderr')
         self.failUnless(re.search(r'no such file', got, re.I) # unix
                         or re.search(r'cannot find the path specified',
@@ -188,12 +170,89 @@
                         "message, got '%s'" % got
                         )
 
-    # todo: interrupt(), kill process
+    def testTimeout(self):
+        args = {'command': [sys.executable, "sleep.py", "10"],
+                'workdir': '.', 'timeout': 2}
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        d.addCallback(self._testTimeout_1)
+        return maybeWait(d)
+    def _testTimeout_1(self, res):
+        self.failIfEqual(self.getrc(), 0)
+        got = self.getfile('header')
+        self.failUnlessIn("command timed out: 2 seconds without output", got)
+        if runtime.platformType == "posix":
+            # the "killing pid" message is not present in windows
+            self.failUnlessIn("killing pid", got)
+        # but the process *ought* to be killed somehow
+        self.failUnlessIn("process killed by signal", got)
+        #print got
+    if runtime.platformType != 'posix':
+        testTimeout.todo = "timeout doesn't appear to work under windows"
+
+    def testInterrupt1(self):
+        args = {'command': [sys.executable, "sleep.py", "10"],
+                'workdir': '.', 'timeout': 20}
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        reactor.callLater(1, c.interrupt)
+        d.addCallback(self._testInterrupt1_1)
+        return maybeWait(d)
+    def _testInterrupt1_1(self, res):
+        self.failIfEqual(self.getrc(), 0)
+        got = self.getfile('header')
+        self.failUnlessIn("command interrupted", got)
+        if runtime.platformType == "posix":
+            self.failUnlessIn("process killed by signal", got)
+
 
     # todo: twisted-specific command tests
 
+class Shell(ShellBase, unittest.TestCase):
+    usePTY = False
+
+    def testInterrupt2(self):
+        # test the backup timeout. This doesn't work under a PTY, because the
+        # transport.loseConnection we do in the timeout handler actually
+        # *does* kill the process.
+        args = {'command': [sys.executable, "sleep.py", "5"],
+                'workdir': '.', 'timeout': 20}
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        c.command.BACKUP_TIMEOUT = 1
+        # make it unable to kill the child, by changing the signal it uses
+        # from SIGKILL to the do-nothing signal 0.
+        c.command.KILL = None
+        reactor.callLater(1, c.interrupt)
+        d.addBoth(self._testInterrupt2_1)
+        return maybeWait(d)
+    def _testInterrupt2_1(self, res):
+        # the slave should raise a TimeoutError exception. In a normal build
+        # process (i.e. one that uses step.RemoteShellCommand), this
+        # exception will be handed to the Step, which will acquire an ERROR
+        # status. In our test environment, it isn't such a big deal.
+        self.failUnless(isinstance(res, failure.Failure),
+                        "res is not a Failure: %s" % (res,))
+        self.failUnless(res.check(commands.TimeoutError))
+        self.checkrc(-1)
+        return
+        # the command is still actually running. Start another command, to
+        # make sure that a) the old command's output doesn't interfere with
+        # the new one, and b) the old command's actual termination doesn't
+        # break anything
+        args = {'command': [sys.executable, "sleep.py", "5"],
+                'workdir': '.', 'timeout': 20}
+        c = SlaveShellCommand(self.builder, None, args)
+        d = c.start()
+        d.addCallback(self._testInterrupt2_2)
+        return d
+    def _testInterrupt2_2(self, res):
+        self.checkrc(0)
+        # N.B.: under windows, the trial process hangs out for another few
+        # seconds. I assume that the win32eventreactor is waiting for one of
+        # the lingering child processes to really finish.
 
 if runtime.platformType == 'posix':
     # test with PTYs also
-    class ShellPTY(Shell):
+    class ShellPTY(ShellBase, unittest.TestCase):
         usePTY = True





More information about the Commits mailing list