remove a lot of unused imports, marked by pyflakes
[buildbot.git] / buildbot / test / test_slavecommand.py
blob613b40c34c202f1a8ca381d2b52b3c832c38e933
1 # -*- test-case-name: buildbot.test.test_slavecommand -*-
3 from twisted.trial import unittest
4 from twisted.internet import reactor, interfaces
5 from twisted.python import runtime, failure, util
6 from buildbot.twcompat import maybeWait
8 import os, sys
10 from buildbot.slave import commands
11 SlaveShellCommand = commands.SlaveShellCommand
13 from buildbot.test.runutils import SignalMixin, FakeSlaveBuilder
15 # test slavecommand.py by running the various commands with a fake
16 # SlaveBuilder object that logs the calls to sendUpdate()
20 class ShellBase(SignalMixin):
22 def setUp(self):
23 self.basedir = "test_slavecommand"
24 if not os.path.isdir(self.basedir):
25 os.mkdir(self.basedir)
26 self.subdir = os.path.join(self.basedir, "subdir")
27 if not os.path.isdir(self.subdir):
28 os.mkdir(self.subdir)
29 self.builder = FakeSlaveBuilder(self.usePTY, self.basedir)
30 self.emitcmd = util.sibpath(__file__, "emit.py")
31 self.subemitcmd = os.path.join(util.sibpath(__file__, "subdir"),
32 "emit.py")
33 self.sleepcmd = util.sibpath(__file__, "sleep.py")
35 def failUnlessIn(self, substring, string):
36 self.failUnless(string.find(substring) != -1,
37 "'%s' not in '%s'" % (substring, string))
39 def getfile(self, which):
40 got = ""
41 for r in self.builder.updates:
42 if r.has_key(which):
43 got += r[which]
44 return got
46 def checkOutput(self, expected):
47 """
48 @type expected: list of (streamname, contents) tuples
49 @param expected: the expected output
50 """
51 expected_linesep = os.linesep
52 if self.usePTY:
53 # PTYs change the line ending. I'm not sure why.
54 expected_linesep = "\r\n"
55 expected = [(stream, contents.replace("\n", expected_linesep, 1000))
56 for (stream, contents) in expected]
57 if self.usePTY:
58 # PTYs merge stdout+stderr into a single stream
59 expected = [('stdout', contents)
60 for (stream, contents) in expected]
61 # now merge everything into one string per stream
62 streams = {}
63 for (stream, contents) in expected:
64 streams[stream] = streams.get(stream, "") + contents
65 for (stream, contents) in streams.items():
66 got = self.getfile(stream)
67 self.assertEquals(got, contents)
69 def getrc(self):
70 self.failUnless(self.builder.updates[-1].has_key('rc'))
71 got = self.builder.updates[-1]['rc']
72 return got
73 def checkrc(self, expected):
74 got = self.getrc()
75 self.assertEquals(got, expected)
77 def testShell1(self):
78 targetfile = os.path.join(self.basedir, "log1.out")
79 if os.path.exists(targetfile):
80 os.unlink(targetfile)
81 cmd = "%s %s 0" % (sys.executable, self.emitcmd)
82 args = {'command': cmd, 'workdir': '.', 'timeout': 60}
83 c = SlaveShellCommand(self.builder, None, args)
84 d = c.start()
85 expected = [('stdout', "this is stdout\n"),
86 ('stderr', "this is stderr\n")]
87 d.addCallback(self._checkPass, expected, 0)
88 def _check_targetfile(res):
89 self.failUnless(os.path.exists(targetfile))
90 d.addCallback(_check_targetfile)
91 return maybeWait(d)
93 def _checkPass(self, res, expected, rc):
94 self.checkOutput(expected)
95 self.checkrc(rc)
97 def testShell2(self):
98 cmd = [sys.executable, self.emitcmd, "0"]
99 args = {'command': cmd, 'workdir': '.', 'timeout': 60}
100 c = SlaveShellCommand(self.builder, None, args)
101 d = c.start()
102 expected = [('stdout', "this is stdout\n"),
103 ('stderr', "this is stderr\n")]
104 d.addCallback(self._checkPass, expected, 0)
105 return maybeWait(d)
107 def testShellRC(self):
108 cmd = [sys.executable, self.emitcmd, "1"]
109 args = {'command': cmd, 'workdir': '.', 'timeout': 60}
110 c = SlaveShellCommand(self.builder, None, args)
111 d = c.start()
112 expected = [('stdout', "this is stdout\n"),
113 ('stderr', "this is stderr\n")]
114 d.addCallback(self._checkPass, expected, 1)
115 return maybeWait(d)
117 def testShellEnv(self):
118 cmd = "%s %s 0" % (sys.executable, self.emitcmd)
119 args = {'command': cmd, 'workdir': '.',
120 'env': {'EMIT_TEST': "envtest"}, 'timeout': 60}
121 c = SlaveShellCommand(self.builder, None, args)
122 d = c.start()
123 expected = [('stdout', "this is stdout\n"),
124 ('stderr', "this is stderr\n"),
125 ('stdout', "EMIT_TEST: envtest\n"),
127 d.addCallback(self._checkPass, expected, 0)
128 return maybeWait(d)
130 def testShellSubdir(self):
131 targetfile = os.path.join(self.basedir, "subdir", "log1.out")
132 if os.path.exists(targetfile):
133 os.unlink(targetfile)
134 cmd = "%s %s 0" % (sys.executable, self.subemitcmd)
135 args = {'command': cmd, 'workdir': "subdir", 'timeout': 60}
136 c = SlaveShellCommand(self.builder, None, args)
137 d = c.start()
138 expected = [('stdout', "this is stdout in subdir\n"),
139 ('stderr', "this is stderr\n")]
140 d.addCallback(self._checkPass, expected, 0)
141 def _check_targetfile(res):
142 self.failUnless(os.path.exists(targetfile))
143 d.addCallback(_check_targetfile)
144 return maybeWait(d)
146 def testShellMissingCommand(self):
147 args = {'command': "/bin/EndWorldHungerAndMakePigsFly",
148 'workdir': '.', 'timeout': 10,
149 'env': {"LC_ALL": "C"},
151 c = SlaveShellCommand(self.builder, None, args)
152 d = c.start()
153 d.addCallback(self._testShellMissingCommand_1)
154 return maybeWait(d)
155 def _testShellMissingCommand_1(self, res):
156 self.failIfEqual(self.getrc(), 0)
157 # we used to check the error message to make sure it said something
158 # about a missing command, but there are a variety of shells out
159 # there, and they emit message sin a variety of languages, so we
160 # stopped trying.
162 def testTimeout(self):
163 args = {'command': [sys.executable, self.sleepcmd, "10"],
164 'workdir': '.', 'timeout': 2}
165 c = SlaveShellCommand(self.builder, None, args)
166 d = c.start()
167 d.addCallback(self._testTimeout_1)
168 return maybeWait(d)
169 def _testTimeout_1(self, res):
170 self.failIfEqual(self.getrc(), 0)
171 got = self.getfile('header')
172 self.failUnlessIn("command timed out: 2 seconds without output", got)
173 if runtime.platformType == "posix":
174 # the "killing pid" message is not present in windows
175 self.failUnlessIn("killing pid", got)
176 # but the process *ought* to be killed somehow
177 self.failUnlessIn("process killed by signal", got)
178 #print got
179 if runtime.platformType != 'posix':
180 testTimeout.todo = "timeout doesn't appear to work under windows"
182 def testInterrupt1(self):
183 args = {'command': [sys.executable, self.sleepcmd, "10"],
184 'workdir': '.', 'timeout': 20}
185 c = SlaveShellCommand(self.builder, None, args)
186 d = c.start()
187 reactor.callLater(1, c.interrupt)
188 d.addCallback(self._testInterrupt1_1)
189 return maybeWait(d)
190 def _testInterrupt1_1(self, res):
191 self.failIfEqual(self.getrc(), 0)
192 got = self.getfile('header')
193 self.failUnlessIn("command interrupted", got)
194 if runtime.platformType == "posix":
195 self.failUnlessIn("process killed by signal", got)
196 if runtime.platformType != 'posix':
197 testInterrupt1.todo = "interrupt doesn't appear to work under windows"
200 # todo: twisted-specific command tests
202 class Shell(ShellBase, unittest.TestCase):
203 usePTY = False
205 def testInterrupt2(self):
206 # test the backup timeout. This doesn't work under a PTY, because the
207 # transport.loseConnection we do in the timeout handler actually
208 # *does* kill the process.
209 args = {'command': [sys.executable, self.sleepcmd, "5"],
210 'workdir': '.', 'timeout': 20}
211 c = SlaveShellCommand(self.builder, None, args)
212 d = c.start()
213 c.command.BACKUP_TIMEOUT = 1
214 # make it unable to kill the child, by changing the signal it uses
215 # from SIGKILL to the do-nothing signal 0.
216 c.command.KILL = None
217 reactor.callLater(1, c.interrupt)
218 d.addBoth(self._testInterrupt2_1)
219 return maybeWait(d)
220 def _testInterrupt2_1(self, res):
221 # the slave should raise a TimeoutError exception. In a normal build
222 # process (i.e. one that uses step.RemoteShellCommand), this
223 # exception will be handed to the Step, which will acquire an ERROR
224 # status. In our test environment, it isn't such a big deal.
225 self.failUnless(isinstance(res, failure.Failure),
226 "res is not a Failure: %s" % (res,))
227 self.failUnless(res.check(commands.TimeoutError))
228 self.checkrc(-1)
229 return
230 # the command is still actually running. Start another command, to
231 # make sure that a) the old command's output doesn't interfere with
232 # the new one, and b) the old command's actual termination doesn't
233 # break anything
234 args = {'command': [sys.executable, self.sleepcmd, "5"],
235 'workdir': '.', 'timeout': 20}
236 c = SlaveShellCommand(self.builder, None, args)
237 d = c.start()
238 d.addCallback(self._testInterrupt2_2)
239 return d
240 def _testInterrupt2_2(self, res):
241 self.checkrc(0)
242 # N.B.: under windows, the trial process hangs out for another few
243 # seconds. I assume that the win32eventreactor is waiting for one of
244 # the lingering child processes to really finish.
246 haveProcess = interfaces.IReactorProcess(reactor, None)
247 if runtime.platformType == 'posix':
248 # test with PTYs also
249 class ShellPTY(ShellBase, unittest.TestCase):
250 usePTY = True
251 if not haveProcess:
252 ShellPTY.skip = "this reactor doesn't support IReactorProcess"
253 if not haveProcess:
254 Shell.skip = "this reactor doesn't support IReactorProcess"