Add MasterShellCommand
[buildbot.git] / buildbot / test / test_steps.py
blob3c7587d467870a653ae5f76b8eae3c9f2216e619
1 # -*- test-case-name: buildbot.test.test_steps -*-
3 # create the BuildStep with a fake .remote instance that logs the
4 # .callRemote invocations and compares them against the expected calls. Then
5 # the test harness should send statusUpdate() messages in with assorted
6 # data, eventually calling remote_complete(). Then we can verify that the
7 # Step's rc was correct, and that the status it was supposed to return
8 # matches.
10 # sometimes, .callRemote should raise an exception because of a stale
11 # reference. Sometimes it should errBack with an UnknownCommand failure.
12 # Or other failure.
14 # todo: test batched updates, by invoking remote_update(updates) instead of
15 # statusUpdate(update). Also involves interrupted builds.
17 import os
19 from twisted.trial import unittest
20 from twisted.internet import reactor, defer
22 from buildbot.sourcestamp import SourceStamp
23 from buildbot.process import buildstep, base, factory
24 from buildbot.buildslave import BuildSlave
25 from buildbot.steps import shell, source, python, master
26 from buildbot.status import builder
27 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE
28 from buildbot.test.runutils import RunMixin, rmtree
29 from buildbot.test.runutils import makeBuildStep, StepTester
30 from buildbot.slave import commands, registry
33 class MyShellCommand(shell.ShellCommand):
34 started = False
35 def runCommand(self, c):
36 self.started = True
37 self.rc = c
38 return shell.ShellCommand.runCommand(self, c)
40 class FakeBuild:
41 pass
42 class FakeBuilder:
43 statusbag = None
44 name = "fakebuilder"
45 class FakeSlaveBuilder:
46 def getSlaveCommandVersion(self, command, oldversion=None):
47 return "1.10"
49 class FakeRemote:
50 def __init__(self):
51 self.events = []
52 self.remoteCalls = 0
53 #self.callRemoteNotifier = None
54 def callRemote(self, methname, *args):
55 event = ["callRemote", methname, args]
56 self.events.append(event)
57 ## if self.callRemoteNotifier:
58 ## reactor.callLater(0, self.callRemoteNotifier, event)
59 self.remoteCalls += 1
60 self.deferred = defer.Deferred()
61 return self.deferred
62 def notifyOnDisconnect(self, callback):
63 pass
64 def dontNotifyOnDisconnect(self, callback):
65 pass
68 class BuildStep(unittest.TestCase):
70 def setUp(self):
71 rmtree("test_steps")
72 self.builder = FakeBuilder()
73 self.builder_status = builder.BuilderStatus("fakebuilder")
74 self.builder_status.basedir = "test_steps"
75 self.builder_status.nextBuildNumber = 0
76 os.mkdir(self.builder_status.basedir)
77 self.build_status = self.builder_status.newBuild()
78 req = base.BuildRequest("reason", SourceStamp(), 'test_builder')
79 self.build = base.Build([req])
80 self.build.build_status = self.build_status # fake it
81 self.build.builder = self.builder
82 self.build.slavebuilder = FakeSlaveBuilder()
83 self.remote = FakeRemote()
84 self.finished = 0
86 def callback(self, results):
87 self.failed = 0
88 self.failure = None
89 self.results = results
90 self.finished = 1
91 def errback(self, failure):
92 self.failed = 1
93 self.failure = failure
94 self.results = None
95 self.finished = 1
97 def testShellCommand1(self):
98 cmd = "argle bargle"
99 dir = "murkle"
100 self.expectedEvents = []
101 buildstep.RemoteCommand.commandCounter[0] = 3
102 c = MyShellCommand(workdir=dir, command=cmd, timeout=10)
103 c.setBuild(self.build)
104 c.setBuildSlave(BuildSlave("name", "password"))
105 self.assertEqual(self.remote.events, self.expectedEvents)
106 c.step_status = self.build_status.addStepWithName("myshellcommand")
107 d = c.startStep(self.remote)
108 self.failUnless(c.started)
109 d.addCallbacks(self.callback, self.errback)
110 d2 = self.poll()
111 d2.addCallback(self._testShellCommand1_2, c)
112 return d2
113 testShellCommand1.timeout = 10
115 def poll(self, ignored=None):
116 # TODO: This is gross, but at least it's no longer using
117 # reactor.iterate() . Still, get rid of this some day soon.
118 if self.remote.remoteCalls == 0:
119 d = defer.Deferred()
120 d.addCallback(self.poll)
121 reactor.callLater(0.1, d.callback, None)
122 return d
123 return defer.succeed(None)
125 def _testShellCommand1_2(self, res, c):
126 rc = c.rc
127 self.expectedEvents.append(["callRemote", "startCommand",
128 (rc, "3",
129 "shell",
130 {'command': "argle bargle",
131 'workdir': "murkle",
132 'want_stdout': 1,
133 'want_stderr': 1,
134 'logfiles': {},
135 'timeout': 10,
136 'env': None}) ] )
137 self.assertEqual(self.remote.events, self.expectedEvents)
139 # we could do self.remote.deferred.errback(UnknownCommand) here. We
140 # could also do .callback(), but generally the master end silently
141 # ignores the slave's ack
143 logs = c.step_status.getLogs()
144 for log in logs:
145 if log.getName() == "log":
146 break
148 rc.remoteUpdate({'header':
149 "command 'argle bargle' in dir 'murkle'\n\n"})
150 rc.remoteUpdate({'stdout': "foo\n"})
151 self.assertEqual(log.getText(), "foo\n")
152 self.assertEqual(log.getTextWithHeaders(),
153 "command 'argle bargle' in dir 'murkle'\n\n"
154 "foo\n")
155 rc.remoteUpdate({'stderr': "bar\n"})
156 self.assertEqual(log.getText(), "foo\nbar\n")
157 self.assertEqual(log.getTextWithHeaders(),
158 "command 'argle bargle' in dir 'murkle'\n\n"
159 "foo\nbar\n")
160 rc.remoteUpdate({'rc': 0})
161 self.assertEqual(rc.rc, 0)
163 rc.remote_complete()
164 # that should fire the Deferred
165 d = self.poll2()
166 d.addCallback(self._testShellCommand1_3)
167 return d
169 def poll2(self, ignored=None):
170 if not self.finished:
171 d = defer.Deferred()
172 d.addCallback(self.poll2)
173 reactor.callLater(0.1, d.callback, None)
174 return d
175 return defer.succeed(None)
177 def _testShellCommand1_3(self, res):
178 self.assertEqual(self.failed, 0)
179 self.assertEqual(self.results, 0)
182 class MyObserver(buildstep.LogObserver):
183 out = ""
184 def outReceived(self, data):
185 self.out = self.out + data
187 class Steps(unittest.TestCase):
188 def testMultipleStepInstances(self):
189 steps = [
190 (source.CVS, {'cvsroot': "root", 'cvsmodule': "module"}),
191 (shell.Configure, {'command': "./configure"}),
192 (shell.Compile, {'command': "make"}),
193 (shell.Compile, {'command': "make more"}),
194 (shell.Compile, {'command': "make evenmore"}),
195 (shell.Test, {'command': "make test"}),
196 (shell.Test, {'command': "make testharder"}),
198 f = factory.ConfigurableBuildFactory(steps)
199 req = base.BuildRequest("reason", SourceStamp(), 'test_builder')
200 b = f.newBuild([req])
201 #for s in b.steps: print s.name
203 def failUnlessClones(self, s1, attrnames):
204 f1 = s1.getStepFactory()
205 f,args = f1
206 s2 = f(**args)
207 for name in attrnames:
208 self.failUnlessEqual(getattr(s1, name), getattr(s2, name))
210 def clone(self, s1):
211 f1 = s1.getStepFactory()
212 f,args = f1
213 s2 = f(**args)
214 return s2
216 def testClone(self):
217 s1 = shell.ShellCommand(command=["make", "test"],
218 timeout=1234,
219 workdir="here",
220 description="yo",
221 descriptionDone="yoyo",
222 env={'key': 'value'},
223 want_stdout=False,
224 want_stderr=False,
225 logfiles={"name": "filename"},
227 shellparms = (buildstep.BuildStep.parms +
228 ("remote_kwargs description descriptionDone "
229 "command logfiles").split() )
230 self.failUnlessClones(s1, shellparms)
233 # test the various methods available to buildsteps
235 def test_getProperty(self):
236 s = makeBuildStep("test_steps.Steps.test_getProperty")
237 bs = s.step_status.getBuild()
239 s.setProperty("prop1", "value1", "test")
240 s.setProperty("prop2", "value2", "test")
241 self.failUnlessEqual(s.getProperty("prop1"), "value1")
242 self.failUnlessEqual(bs.getProperty("prop1"), "value1")
243 self.failUnlessEqual(s.getProperty("prop2"), "value2")
244 self.failUnlessEqual(bs.getProperty("prop2"), "value2")
245 s.setProperty("prop1", "value1a", "test")
246 self.failUnlessEqual(s.getProperty("prop1"), "value1a")
247 self.failUnlessEqual(bs.getProperty("prop1"), "value1a")
250 def test_addURL(self):
251 s = makeBuildStep("test_steps.Steps.test_addURL")
252 s.addURL("coverage", "http://coverage.example.org/target")
253 s.addURL("icon", "http://coverage.example.org/icon.png")
254 bs = s.step_status
255 links = bs.getURLs()
256 expected = {"coverage": "http://coverage.example.org/target",
257 "icon": "http://coverage.example.org/icon.png",
259 self.failUnlessEqual(links, expected)
261 def test_addLog(self):
262 s = makeBuildStep("test_steps.Steps.test_addLog")
263 l = s.addLog("newlog")
264 l.addStdout("some stdout here")
265 l.finish()
266 bs = s.step_status
267 logs = bs.getLogs()
268 self.failUnlessEqual(len(logs), 1)
269 l1 = logs[0]
270 self.failUnlessEqual(l1.getText(), "some stdout here")
271 l1a = s.getLog("newlog")
272 self.failUnlessEqual(l1a.getText(), "some stdout here")
274 def test_addHTMLLog(self):
275 s = makeBuildStep("test_steps.Steps.test_addHTMLLog")
276 l = s.addHTMLLog("newlog", "some html here")
277 bs = s.step_status
278 logs = bs.getLogs()
279 self.failUnlessEqual(len(logs), 1)
280 l1 = logs[0]
281 self.failUnless(isinstance(l1, builder.HTMLLogFile))
282 self.failUnlessEqual(l1.getText(), "some html here")
284 def test_addCompleteLog(self):
285 s = makeBuildStep("test_steps.Steps.test_addCompleteLog")
286 l = s.addCompleteLog("newlog", "some stdout here")
287 bs = s.step_status
288 logs = bs.getLogs()
289 self.failUnlessEqual(len(logs), 1)
290 l1 = logs[0]
291 self.failUnlessEqual(l1.getText(), "some stdout here")
292 l1a = s.getLog("newlog")
293 self.failUnlessEqual(l1a.getText(), "some stdout here")
295 def test_addLogObserver(self):
296 s = makeBuildStep("test_steps.Steps.test_addLogObserver")
297 bss = s.step_status
298 o1,o2,o3 = MyObserver(), MyObserver(), MyObserver()
300 # add the log before the observer
301 l1 = s.addLog("one")
302 l1.addStdout("onestuff")
303 s.addLogObserver("one", o1)
304 self.failUnlessEqual(o1.out, "onestuff")
305 l1.addStdout(" morestuff")
306 self.failUnlessEqual(o1.out, "onestuff morestuff")
308 # add the observer before the log
309 s.addLogObserver("two", o2)
310 l2 = s.addLog("two")
311 l2.addStdout("twostuff")
312 self.failUnlessEqual(o2.out, "twostuff")
314 # test more stuff about ShellCommands
316 def test_description(self):
317 s = makeBuildStep("test_steps.Steps.test_description.1",
318 step_class=shell.ShellCommand,
319 workdir="dummy",
320 description=["list", "of", "strings"],
321 descriptionDone=["another", "list"])
322 self.failUnlessEqual(s.description, ["list", "of", "strings"])
323 self.failUnlessEqual(s.descriptionDone, ["another", "list"])
325 s = makeBuildStep("test_steps.Steps.test_description.2",
326 step_class=shell.ShellCommand,
327 workdir="dummy",
328 description="single string",
329 descriptionDone="another string")
330 self.failUnlessEqual(s.description, ["single string"])
331 self.failUnlessEqual(s.descriptionDone, ["another string"])
333 class VersionCheckingStep(buildstep.BuildStep):
334 def start(self):
335 # give our test a chance to run. It is non-trivial for a buildstep to
336 # claw its way back out to the test case which is currently running.
337 master = self.build.builder.botmaster.parent
338 checker = master._checker
339 checker(self)
340 # then complete
341 self.finished(buildstep.SUCCESS)
343 version_config = """
344 from buildbot.process import factory
345 from buildbot.test.test_steps import VersionCheckingStep
346 from buildbot.buildslave import BuildSlave
347 BuildmasterConfig = c = {}
348 f1 = factory.BuildFactory([
349 factory.s(VersionCheckingStep),
351 c['slaves'] = [BuildSlave('bot1', 'sekrit')]
352 c['schedulers'] = []
353 c['builders'] = [{'name':'quick', 'slavename':'bot1',
354 'builddir': 'quickdir', 'factory': f1}]
355 c['slavePortnum'] = 0
358 class SlaveVersion(RunMixin, unittest.TestCase):
359 def setUp(self):
360 RunMixin.setUp(self)
361 self.master.loadConfig(version_config)
362 self.master.startService()
363 d = self.connectSlave(["quick"])
364 return d
366 def doBuild(self, buildername):
367 br = base.BuildRequest("forced", SourceStamp(), 'test_builder')
368 d = br.waitUntilFinished()
369 self.control.getBuilder(buildername).requestBuild(br)
370 return d
373 def checkCompare(self, s):
374 cver = commands.command_version
375 v = s.slaveVersion("svn", None)
376 # this insures that we are getting the version correctly
377 self.failUnlessEqual(s.slaveVersion("svn", None), cver)
378 # and that non-existent commands do not provide a version
379 self.failUnlessEqual(s.slaveVersion("NOSUCHCOMMAND"), None)
380 # TODO: verify that a <=0.5.0 buildslave (which does not implement
381 # remote_getCommands) handles oldversion= properly. This requires a
382 # mutant slave which does not offer that method.
383 #self.failUnlessEqual(s.slaveVersion("NOSUCHCOMMAND", "old"), "old")
385 # now check the comparison functions
386 self.failIf(s.slaveVersionIsOlderThan("svn", cver))
387 self.failIf(s.slaveVersionIsOlderThan("svn", "1.1"))
388 self.failUnless(s.slaveVersionIsOlderThan("svn", cver + ".1"))
390 self.failUnlessEqual(s.getSlaveName(), "bot1")
392 def testCompare(self):
393 self.master._checker = self.checkCompare
394 d = self.doBuild("quick")
395 return d
398 class _SimpleBuildStep(buildstep.BuildStep):
399 def start(self):
400 args = {"arg1": "value"}
401 cmd = buildstep.RemoteCommand("simple", args)
402 d = self.runCommand(cmd)
403 d.addCallback(lambda res: self.finished(SUCCESS))
405 class _SimpleCommand(commands.Command):
406 def start(self):
407 self.builder.flag = True
408 self.builder.flag_args = self.args
409 return defer.succeed(None)
411 class CheckStepTester(StepTester, unittest.TestCase):
412 def testSimple(self):
413 self.slavebase = "testSimple.slave"
414 self.masterbase = "testSimple.master"
415 sb = self.makeSlaveBuilder()
416 sb.flag = False
417 registry.registerSlaveCommand("simple", _SimpleCommand, "1")
418 step = self.makeStep(_SimpleBuildStep)
419 d = self.runStep(step)
420 def _checkSimple(results):
421 self.failUnless(sb.flag)
422 self.failUnlessEqual(sb.flag_args, {"arg1": "value"})
423 d.addCallback(_checkSimple)
424 return d
426 class Python(StepTester, unittest.TestCase):
427 def testPyFlakes1(self):
428 self.masterbase = "Python.testPyFlakes1"
429 step = self.makeStep(python.PyFlakes)
430 output = \
431 """pyflakes buildbot
432 buildbot/changes/freshcvsmail.py:5: 'FCMaildirSource' imported but unused
433 buildbot/clients/debug.py:9: redefinition of unused 'gtk' from line 9
434 buildbot/clients/debug.py:9: 'gnome' imported but unused
435 buildbot/scripts/runner.py:323: redefinition of unused 'run' from line 321
436 buildbot/scripts/runner.py:325: redefinition of unused 'run' from line 323
437 buildbot/scripts/imaginary.py:12: undefined name 'size'
438 buildbot/scripts/imaginary.py:18: 'from buildbot import *' used; unable to detect undefined names
440 log = step.addLog("stdio")
441 log.addStdout(output)
442 log.finish()
443 step.createSummary(log)
444 desc = step.descriptionDone
445 self.failUnless("unused=2" in desc)
446 self.failUnless("undefined=1" in desc)
447 self.failUnless("redefs=3" in desc)
448 self.failUnless("import*=1" in desc)
449 self.failIf("misc=" in desc)
451 self.failUnlessEqual(step.getProperty("pyflakes-unused"), 2)
452 self.failUnlessEqual(step.getProperty("pyflakes-undefined"), 1)
453 self.failUnlessEqual(step.getProperty("pyflakes-redefs"), 3)
454 self.failUnlessEqual(step.getProperty("pyflakes-import*"), 1)
455 self.failUnlessEqual(step.getProperty("pyflakes-misc"), 0)
456 self.failUnlessEqual(step.getProperty("pyflakes-total"), 7)
458 logs = {}
459 for log in step.step_status.getLogs():
460 logs[log.getName()] = log
462 for name in ["unused", "undefined", "redefs", "import*"]:
463 self.failUnless(name in logs)
464 self.failIf("misc" in logs)
465 lines = logs["unused"].readlines()
466 self.failUnlessEqual(len(lines), 2)
467 self.failUnlessEqual(lines[0], "buildbot/changes/freshcvsmail.py:5: 'FCMaildirSource' imported but unused\n")
469 cmd = buildstep.RemoteCommand(None, {})
470 cmd.rc = 0
471 results = step.evaluateCommand(cmd)
472 self.failUnlessEqual(results, FAILURE) # because of the 'undefined'
474 def testPyFlakes2(self):
475 self.masterbase = "Python.testPyFlakes2"
476 step = self.makeStep(python.PyFlakes)
477 output = \
478 """pyflakes buildbot
479 some more text here that should be ignored
480 buildbot/changes/freshcvsmail.py:5: 'FCMaildirSource' imported but unused
481 buildbot/clients/debug.py:9: redefinition of unused 'gtk' from line 9
482 buildbot/clients/debug.py:9: 'gnome' imported but unused
483 buildbot/scripts/runner.py:323: redefinition of unused 'run' from line 321
484 buildbot/scripts/runner.py:325: redefinition of unused 'run' from line 323
485 buildbot/scripts/imaginary.py:12: undefined name 'size'
486 could not compile 'blah/blah.py':3:
487 pretend there was an invalid line here
488 buildbot/scripts/imaginary.py:18: 'from buildbot import *' used; unable to detect undefined names
490 log = step.addLog("stdio")
491 log.addStdout(output)
492 log.finish()
493 step.createSummary(log)
494 desc = step.descriptionDone
495 self.failUnless("unused=2" in desc)
496 self.failUnless("undefined=1" in desc)
497 self.failUnless("redefs=3" in desc)
498 self.failUnless("import*=1" in desc)
499 self.failUnless("misc=2" in desc)
502 def testPyFlakes3(self):
503 self.masterbase = "Python.testPyFlakes3"
504 step = self.makeStep(python.PyFlakes)
505 output = \
506 """buildbot/changes/freshcvsmail.py:5: 'FCMaildirSource' imported but unused
507 buildbot/clients/debug.py:9: redefinition of unused 'gtk' from line 9
508 buildbot/clients/debug.py:9: 'gnome' imported but unused
509 buildbot/scripts/runner.py:323: redefinition of unused 'run' from line 321
510 buildbot/scripts/runner.py:325: redefinition of unused 'run' from line 323
511 buildbot/scripts/imaginary.py:12: undefined name 'size'
512 buildbot/scripts/imaginary.py:18: 'from buildbot import *' used; unable to detect undefined names
514 log = step.addLog("stdio")
515 log.addStdout(output)
516 log.finish()
517 step.createSummary(log)
518 desc = step.descriptionDone
519 self.failUnless("unused=2" in desc)
520 self.failUnless("undefined=1" in desc)
521 self.failUnless("redefs=3" in desc)
522 self.failUnless("import*=1" in desc)
523 self.failIf("misc" in desc)
526 class OrdinaryCompile(shell.Compile):
527 warningPattern = "ordinary line"
529 class Warnings(StepTester, unittest.TestCase):
530 def testCompile1(self):
531 self.masterbase = "Warnings.testCompile1"
532 step = self.makeStep(shell.Compile)
533 output = \
534 """Compile started
535 normal line
536 warning: oh noes!
537 ordinary line
538 error (but we aren't looking for errors now, are we)
539 line 23: warning: we are now on line 23
540 ending line
542 log = step.addLog("stdio")
543 log.addStdout(output)
544 log.finish()
545 step.createSummary(log)
546 self.failUnlessEqual(step.getProperty("warnings-count"), 2)
547 logs = {}
548 for log in step.step_status.getLogs():
549 logs[log.getName()] = log
550 self.failUnless("warnings" in logs)
551 lines = logs["warnings"].readlines()
552 self.failUnlessEqual(len(lines), 2)
553 self.failUnlessEqual(lines[0], "warning: oh noes!\n")
554 self.failUnlessEqual(lines[1],
555 "line 23: warning: we are now on line 23\n")
557 cmd = buildstep.RemoteCommand(None, {})
558 cmd.rc = 0
559 results = step.evaluateCommand(cmd)
560 self.failUnlessEqual(results, WARNINGS)
562 def testCompile2(self):
563 self.masterbase = "Warnings.testCompile2"
564 step = self.makeStep(shell.Compile, warningPattern="ordinary line")
565 output = \
566 """Compile started
567 normal line
568 warning: oh noes!
569 ordinary line
570 error (but we aren't looking for errors now, are we)
571 line 23: warning: we are now on line 23
572 ending line
574 log = step.addLog("stdio")
575 log.addStdout(output)
576 log.finish()
577 step.createSummary(log)
578 self.failUnlessEqual(step.getProperty("warnings-count"), 1)
579 logs = {}
580 for log in step.step_status.getLogs():
581 logs[log.getName()] = log
582 self.failUnless("warnings" in logs)
583 lines = logs["warnings"].readlines()
584 self.failUnlessEqual(len(lines), 1)
585 self.failUnlessEqual(lines[0], "ordinary line\n")
587 cmd = buildstep.RemoteCommand(None, {})
588 cmd.rc = 0
589 results = step.evaluateCommand(cmd)
590 self.failUnlessEqual(results, WARNINGS)
592 def testCompile3(self):
593 self.masterbase = "Warnings.testCompile3"
594 step = self.makeStep(OrdinaryCompile)
595 output = \
596 """Compile started
597 normal line
598 warning: oh noes!
599 ordinary line
600 error (but we aren't looking for errors now, are we)
601 line 23: warning: we are now on line 23
602 ending line
604 step.setProperty("warnings-count", 10, "test")
605 log = step.addLog("stdio")
606 log.addStdout(output)
607 log.finish()
608 step.createSummary(log)
609 self.failUnlessEqual(step.getProperty("warnings-count"), 11)
610 logs = {}
611 for log in step.step_status.getLogs():
612 logs[log.getName()] = log
613 self.failUnless("warnings" in logs)
614 lines = logs["warnings"].readlines()
615 self.failUnlessEqual(len(lines), 1)
616 self.failUnlessEqual(lines[0], "ordinary line\n")
618 cmd = buildstep.RemoteCommand(None, {})
619 cmd.rc = 0
620 results = step.evaluateCommand(cmd)
621 self.failUnlessEqual(results, WARNINGS)
624 class TreeSize(StepTester, unittest.TestCase):
625 def testTreeSize(self):
626 self.slavebase = "TreeSize.testTreeSize.slave"
627 self.masterbase = "TreeSize.testTreeSize.master"
629 sb = self.makeSlaveBuilder()
630 step = self.makeStep(shell.TreeSize)
631 d = self.runStep(step)
632 def _check(results):
633 self.failUnlessEqual(results, SUCCESS)
634 kib = step.getProperty("tree-size-KiB")
635 self.failUnless(isinstance(kib, int))
636 self.failUnless(kib < 100) # should be empty, I get '4'
637 s = step.step_status
638 self.failUnlessEqual(" ".join(s.getText()),
639 "treesize %d KiB" % kib)
640 d.addCallback(_check)
641 return d
643 class FakeCommand:
644 def __init__(self, rc):
645 self.rc = rc
647 class PerlModuleTest(StepTester, unittest.TestCase):
648 def testAllTestsPassed(self):
649 self.masterbase = "PMT.testAllTestsPassed"
650 step = self.makeStep(shell.PerlModuleTest)
651 output = \
652 """ok 1
653 ok 2
654 All tests successful
655 Files=1, Tests=123, other stuff
657 log = step.addLog("stdio")
658 log.addStdout(output)
659 log.finish()
660 rc = step.evaluateCommand(FakeCommand(rc=241))
661 self.failUnlessEqual(rc, SUCCESS)
662 ss = step.step_status
663 self.failUnlessEqual(ss.getStatistic('tests-failed'), 0)
664 self.failUnlessEqual(ss.getStatistic('tests-total'), 123)
665 self.failUnlessEqual(ss.getStatistic('tests-passed'), 123)
667 def testFailures_OldTestHarness(self):
668 self.masterbase = "PMT.testFailures_OldTestHarness"
669 step = self.makeStep(shell.PerlModuleTest)
670 output = \
672 ok 1
673 ok 2
674 3/7 subtests failed
676 log = step.addLog("stdio")
677 log.addStdout(output)
678 log.finish()
679 rc = step.evaluateCommand(FakeCommand(rc = 123))
680 self.failUnlessEqual(rc, FAILURE)
681 ss = step.step_status
682 self.failUnlessEqual(ss.getStatistic('tests-failed'), 3)
683 self.failUnlessEqual(ss.getStatistic('tests-total'), 7)
684 self.failUnlessEqual(ss.getStatistic('tests-passed'), 4)
686 def testFailures_UnparseableStdio(self):
687 self.masterbase = "PMT.testFailures_UnparseableStdio"
688 step = self.makeStep(shell.PerlModuleTest)
689 output = \
691 just some random stuff, you know
693 log = step.addLog("stdio")
694 log.addStdout(output)
695 log.finish()
696 rc = step.evaluateCommand(FakeCommand(rc = 243))
697 self.failUnlessEqual(rc, 243)
698 ss = step.step_status
699 self.failUnlessEqual(ss.getStatistic('tests-failed'), None)
700 self.failUnlessEqual(ss.getStatistic('tests-total'), None)
701 self.failUnlessEqual(ss.getStatistic('tests-passed'), None)
703 def testFailures_NewTestHarness(self):
704 self.masterbase = "PMT.testFailures_NewTestHarness"
705 step = self.makeStep(shell.PerlModuleTest)
706 output = \
708 # Looks like you failed 15 tests of 18.
709 tests/services.......................... Failed 265/30904 subtests
710 (less 16 skipped subtests: 30623 okay)
711 tests/simple_query_backend..............ok
712 tests/simple_query_middleware...........ok
713 tests/soap_globalcollect................ok
714 tests/three_d_me........................ok
715 tests/three_d_me_callback...............ok
716 tests/transaction_create................ok
717 tests/unique_txid.......................ok
719 Test Summary Report
720 -------------------
721 tests/000policies (Wstat: 5632 Tests: 9078 Failed: 22)
722 Failed tests: 2409, 2896-2897, 2900-2901, 2940-2941, 2944-2945
723 2961-2962, 2965-2966, 2969-2970, 2997-2998
724 3262, 3281-3282, 3288-3289
725 Non-zero exit status: 22
726 tests/services (Wstat: 0 Tests: 30904 Failed: 265)
727 Failed tests: 14, 16-21, 64-69, 71-96, 98, 30157, 30159
728 30310, 30316, 30439-30543, 30564, 30566-30577
729 30602, 30604-30607, 30609-30612, 30655
730 30657-30668, 30675, 30697-30716, 30718-30720
731 30722-30736, 30773-30774, 30776-30777, 30786
732 30791, 30795, 30797, 30801, 30822-30827
733 30830-30831, 30848-30855, 30858-30859, 30888-30899
734 30901, 30903-30904
735 Files=68, Tests=264809, 1944 wallclock secs (17.59 usr 0.63 sys + 470.04 cusr 131.40 csys = 619.66 CPU)
736 Result: FAIL
738 log = step.addLog("stdio")
739 log.addStdout(output)
740 log.finish()
741 rc = step.evaluateCommand(FakeCommand(rc=87))
742 self.failUnlessEqual(rc, FAILURE)
743 ss = step.step_status
744 self.failUnlessEqual(ss.getStatistic('tests-failed'), 287)
745 self.failUnlessEqual(ss.getStatistic('tests-total'), 264809)
746 self.failUnlessEqual(ss.getStatistic('tests-passed'), 264522)
748 class MasterShellCommand(StepTester, unittest.TestCase):
749 def testMasterShellCommand(self):
750 self.slavebase = "testMasterShellCommand.slave"
751 self.masterbase = "testMasterShellCommand.master"
752 sb = self.makeSlaveBuilder()
753 step = self.makeStep(master.MasterShellCommand, command=['echo', 'hi'])
755 # we can't invoke runStep until the reactor is started .. hence this
756 # little dance
757 d = defer.Deferred()
758 def _dotest(_):
759 return self.runStep(step)
760 d.addCallback(_dotest)
762 def _check(results):
763 self.failUnlessEqual(results, SUCCESS)
764 logtxt = step.getLog("stdio").getText()
765 self.failUnlessEqual(logtxt.strip(), "hi")
766 d.addCallback(_check)
767 reactor.callLater(0, d.callback, None)
768 return d
770 def testMasterShellCommand_badexit(self):
771 self.slavebase = "testMasterShellCommand_badexit.slave"
772 self.masterbase = "testMasterShellCommand_badexit.master"
773 sb = self.makeSlaveBuilder()
774 step = self.makeStep(master.MasterShellCommand, command="exit 1")
776 # we can't invoke runStep until the reactor is started .. hence this
777 # little dance
778 d = defer.Deferred()
779 def _dotest(_):
780 return self.runStep(step)
781 d.addCallback(_dotest)
783 def _check(results):
784 self.failUnlessEqual(results, FAILURE)
785 d.addCallback(_check)
786 reactor.callLater(0, d.callback, None)
787 return d