OLD | NEW |
| (Empty) |
1 # -*- test-case-name: buildbot.test.test_steps -*- | |
2 | |
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. | |
9 | |
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. | |
13 | |
14 # todo: test batched updates, by invoking remote_update(updates) instead of | |
15 # statusUpdate(update). Also involves interrupted builds. | |
16 | |
17 import sys | |
18 import os | |
19 | |
20 from twisted.trial import unittest | |
21 from twisted.internet import reactor, defer | |
22 | |
23 from buildbot.sourcestamp import SourceStamp | |
24 from buildbot.process import buildstep, base, factory | |
25 from buildbot.process.properties import Properties, WithProperties | |
26 from buildbot.buildslave import BuildSlave | |
27 from buildbot.steps import shell, source, python, master | |
28 from buildbot.status import builder | |
29 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, SKIPPED | |
30 from buildbot.test.runutils import RunMixin, rmtree | |
31 from buildbot.test.runutils import makeBuildStep, StepTester | |
32 from buildbot.slave import commands, registry | |
33 | |
34 | |
35 class MyShellCommand(shell.ShellCommand): | |
36 started = False | |
37 def runCommand(self, c): | |
38 self.started = True | |
39 self.rc = c | |
40 return shell.ShellCommand.runCommand(self, c) | |
41 | |
42 class FakeBuild: | |
43 pass | |
44 class FakeBuilder: | |
45 statusbag = None | |
46 name = "fakebuilder" | |
47 class FakeSlaveBuilder: | |
48 def getSlaveCommandVersion(self, command, oldversion=None): | |
49 return "1.10" | |
50 | |
51 class FakeRemote: | |
52 def __init__(self): | |
53 self.events = [] | |
54 self.remoteCalls = 0 | |
55 #self.callRemoteNotifier = None | |
56 def callRemote(self, methname, *args): | |
57 event = ["callRemote", methname, args] | |
58 self.events.append(event) | |
59 ## if self.callRemoteNotifier: | |
60 ## reactor.callLater(0, self.callRemoteNotifier, event) | |
61 self.remoteCalls += 1 | |
62 self.deferred = defer.Deferred() | |
63 return self.deferred | |
64 def notifyOnDisconnect(self, callback): | |
65 pass | |
66 def dontNotifyOnDisconnect(self, callback): | |
67 pass | |
68 | |
69 | |
70 class BuildStep(unittest.TestCase): | |
71 | |
72 def setUp(self): | |
73 rmtree("test_steps") | |
74 self.builder = FakeBuilder() | |
75 self.builder_status = builder.BuilderStatus("fakebuilder") | |
76 self.builder_status.basedir = "test_steps" | |
77 self.builder_status.nextBuildNumber = 0 | |
78 os.mkdir(self.builder_status.basedir) | |
79 self.build_status = self.builder_status.newBuild() | |
80 req = base.BuildRequest("reason", SourceStamp(), 'test_builder') | |
81 self.build = base.Build([req]) | |
82 self.build.build_status = self.build_status # fake it | |
83 self.build.builder = self.builder | |
84 self.build.slavebuilder = FakeSlaveBuilder() | |
85 self.remote = FakeRemote() | |
86 self.finished = 0 | |
87 | |
88 def callback(self, results): | |
89 self.failed = 0 | |
90 self.failure = None | |
91 self.results = results | |
92 self.finished = 1 | |
93 def errback(self, failure): | |
94 self.failed = 1 | |
95 self.failure = failure | |
96 self.results = None | |
97 self.finished = 1 | |
98 | |
99 def testShellCommand1(self): | |
100 cmd = "argle bargle" | |
101 dir = "murkle" | |
102 self.expectedEvents = [] | |
103 buildstep.RemoteCommand.commandCounter[0] = 3 | |
104 c = MyShellCommand(workdir=dir, command=cmd, timeout=10) | |
105 c.setBuild(self.build) | |
106 c.setBuildSlave(BuildSlave("name", "password")) | |
107 self.assertEqual(self.remote.events, self.expectedEvents) | |
108 c.step_status = self.build_status.addStepWithName("myshellcommand") | |
109 d = c.startStep(self.remote) | |
110 self.failUnless(c.started) | |
111 d.addCallbacks(self.callback, self.errback) | |
112 d2 = self.poll() | |
113 d2.addCallback(self._testShellCommand1_2, c) | |
114 return d2 | |
115 testShellCommand1.timeout = 10 | |
116 | |
117 def poll(self, ignored=None): | |
118 # TODO: This is gross, but at least it's no longer using | |
119 # reactor.iterate() . Still, get rid of this some day soon. | |
120 if self.remote.remoteCalls == 0: | |
121 d = defer.Deferred() | |
122 d.addCallback(self.poll) | |
123 reactor.callLater(0.1, d.callback, None) | |
124 return d | |
125 return defer.succeed(None) | |
126 | |
127 def _testShellCommand1_2(self, res, c): | |
128 rc = c.rc | |
129 self.expectedEvents.append(["callRemote", "startCommand", | |
130 (rc, "3", | |
131 "shell", | |
132 {'command': "argle bargle", | |
133 'workdir': "murkle", | |
134 'logEnviron' : True, | |
135 'want_stdout': 1, | |
136 'want_stderr': 1, | |
137 'logfiles': {}, | |
138 'timeout': 10, | |
139 'maxTime': None, | |
140 'usePTY': 'slave-config', | |
141 'env': None}) ] ) | |
142 self.assertEqual(self.remote.events, self.expectedEvents) | |
143 | |
144 # we could do self.remote.deferred.errback(UnknownCommand) here. We | |
145 # could also do .callback(), but generally the master end silently | |
146 # ignores the slave's ack | |
147 | |
148 logs = c.step_status.getLogs() | |
149 for log in logs: | |
150 if log.getName() == "log": | |
151 break | |
152 | |
153 rc.remoteUpdate({'header': | |
154 "command 'argle bargle' in dir 'murkle'\n\n"}) | |
155 rc.remoteUpdate({'stdout': "foo\n"}) | |
156 self.assertEqual(log.getText(), "foo\n") | |
157 self.assertEqual(log.getTextWithHeaders(), | |
158 "command 'argle bargle' in dir 'murkle'\n\n" | |
159 "foo\n") | |
160 rc.remoteUpdate({'stderr': "bar\n"}) | |
161 self.assertEqual(log.getText(), "foo\nbar\n") | |
162 self.assertEqual(log.getTextWithHeaders(), | |
163 "command 'argle bargle' in dir 'murkle'\n\n" | |
164 "foo\nbar\n") | |
165 rc.remoteUpdate({'rc': 0}) | |
166 self.assertEqual(rc.rc, 0) | |
167 | |
168 rc.remote_complete() | |
169 # that should fire the Deferred | |
170 d = self.poll2() | |
171 d.addCallback(self._testShellCommand1_3) | |
172 return d | |
173 | |
174 def poll2(self, ignored=None): | |
175 if not self.finished: | |
176 d = defer.Deferred() | |
177 d.addCallback(self.poll2) | |
178 reactor.callLater(0.1, d.callback, None) | |
179 return d | |
180 return defer.succeed(None) | |
181 | |
182 def _testShellCommand1_3(self, res): | |
183 self.assertEqual(self.failed, 0) | |
184 self.assertEqual(self.results, 0) | |
185 | |
186 | |
187 class MyObserver(buildstep.LogObserver): | |
188 out = "" | |
189 def outReceived(self, data): | |
190 self.out = self.out + data | |
191 | |
192 class Steps(unittest.TestCase): | |
193 def testMultipleStepInstances(self): | |
194 steps = [ | |
195 (source.CVS, {'cvsroot': "root", 'cvsmodule': "module"}), | |
196 (shell.Configure, {'command': "./configure"}), | |
197 (shell.Compile, {'command': "make"}), | |
198 (shell.Compile, {'command': "make more"}), | |
199 (shell.Compile, {'command': "make evenmore"}), | |
200 (shell.Test, {'command': "make test"}), | |
201 (shell.Test, {'command': "make testharder"}), | |
202 ] | |
203 f = factory.ConfigurableBuildFactory(steps) | |
204 req = base.BuildRequest("reason", SourceStamp(), 'test_builder') | |
205 b = f.newBuild([req]) | |
206 #for s in b.steps: print s.name | |
207 | |
208 def failUnlessClones(self, s1, attrnames): | |
209 f1 = s1.getStepFactory() | |
210 f,args = f1 | |
211 s2 = f(**args) | |
212 for name in attrnames: | |
213 self.failUnlessEqual(getattr(s1, name), getattr(s2, name)) | |
214 | |
215 def clone(self, s1): | |
216 f1 = s1.getStepFactory() | |
217 f,args = f1 | |
218 s2 = f(**args) | |
219 return s2 | |
220 | |
221 def testClone(self): | |
222 s1 = shell.ShellCommand(command=["make", "test"], | |
223 timeout=1234, | |
224 workdir="here", | |
225 description="yo", | |
226 descriptionDone="yoyo", | |
227 env={'key': 'value'}, | |
228 want_stdout=False, | |
229 want_stderr=False, | |
230 logfiles={"name": "filename"}, | |
231 ) | |
232 shellparms = (buildstep.BuildStep.parms + | |
233 ("remote_kwargs description descriptionDone " | |
234 "command logfiles").split() ) | |
235 self.failUnlessClones(s1, shellparms) | |
236 | |
237 | |
238 # test the various methods available to buildsteps | |
239 | |
240 def test_getProperty(self): | |
241 s = makeBuildStep("test_steps.Steps.test_getProperty") | |
242 bs = s.step_status.getBuild() | |
243 | |
244 s.setProperty("prop1", "value1", "test") | |
245 s.setProperty("prop2", "value2", "test") | |
246 self.failUnlessEqual(s.getProperty("prop1"), "value1") | |
247 self.failUnlessEqual(bs.getProperty("prop1"), "value1") | |
248 self.failUnlessEqual(s.getProperty("prop2"), "value2") | |
249 self.failUnlessEqual(bs.getProperty("prop2"), "value2") | |
250 s.setProperty("prop1", "value1a", "test") | |
251 self.failUnlessEqual(s.getProperty("prop1"), "value1a") | |
252 self.failUnlessEqual(bs.getProperty("prop1"), "value1a") | |
253 | |
254 | |
255 def test_addURL(self): | |
256 s = makeBuildStep("test_steps.Steps.test_addURL") | |
257 s.addURL("coverage", "http://coverage.example.org/target") | |
258 s.addURL("icon", "http://coverage.example.org/icon.png") | |
259 bs = s.step_status | |
260 links = bs.getURLs() | |
261 expected = {"coverage": "http://coverage.example.org/target", | |
262 "icon": "http://coverage.example.org/icon.png", | |
263 } | |
264 self.failUnlessEqual(links, expected) | |
265 | |
266 def test_addLog(self): | |
267 s = makeBuildStep("test_steps.Steps.test_addLog") | |
268 l = s.addLog("newlog") | |
269 l.addStdout("some stdout here") | |
270 l.finish() | |
271 bs = s.step_status | |
272 logs = bs.getLogs() | |
273 self.failUnlessEqual(len(logs), 1) | |
274 l1 = logs[0] | |
275 self.failUnlessEqual(l1.getText(), "some stdout here") | |
276 l1a = s.getLog("newlog") | |
277 self.failUnlessEqual(l1a.getText(), "some stdout here") | |
278 | |
279 def test_addHTMLLog(self): | |
280 s = makeBuildStep("test_steps.Steps.test_addHTMLLog") | |
281 l = s.addHTMLLog("newlog", "some html here") | |
282 bs = s.step_status | |
283 logs = bs.getLogs() | |
284 self.failUnlessEqual(len(logs), 1) | |
285 l1 = logs[0] | |
286 self.failUnless(isinstance(l1, builder.HTMLLogFile)) | |
287 self.failUnlessEqual(l1.getText(), "some html here") | |
288 | |
289 def test_addCompleteLog(self): | |
290 s = makeBuildStep("test_steps.Steps.test_addCompleteLog") | |
291 l = s.addCompleteLog("newlog", "some stdout here") | |
292 bs = s.step_status | |
293 logs = bs.getLogs() | |
294 self.failUnlessEqual(len(logs), 1) | |
295 l1 = logs[0] | |
296 self.failUnlessEqual(l1.getText(), "some stdout here") | |
297 l1a = s.getLog("newlog") | |
298 self.failUnlessEqual(l1a.getText(), "some stdout here") | |
299 | |
300 def test_addLogObserver(self): | |
301 s = makeBuildStep("test_steps.Steps.test_addLogObserver") | |
302 bss = s.step_status | |
303 o1,o2,o3 = MyObserver(), MyObserver(), MyObserver() | |
304 | |
305 # add the log before the observer | |
306 l1 = s.addLog("one") | |
307 l1.addStdout("onestuff") | |
308 s.addLogObserver("one", o1) | |
309 self.failUnlessEqual(o1.out, "onestuff") | |
310 l1.addStdout(" morestuff") | |
311 self.failUnlessEqual(o1.out, "onestuff morestuff") | |
312 | |
313 # add the observer before the log | |
314 s.addLogObserver("two", o2) | |
315 l2 = s.addLog("two") | |
316 l2.addStdout("twostuff") | |
317 self.failUnlessEqual(o2.out, "twostuff") | |
318 | |
319 # test more stuff about ShellCommands | |
320 | |
321 def test_description(self): | |
322 s = makeBuildStep("test_steps.Steps.test_description.1", | |
323 step_class=shell.ShellCommand, | |
324 workdir="dummy", | |
325 description=["list", "of", "strings"], | |
326 descriptionDone=["another", "list"]) | |
327 self.failUnlessEqual(s.description, ["list", "of", "strings"]) | |
328 self.failUnlessEqual(s.descriptionDone, ["another", "list"]) | |
329 | |
330 s = makeBuildStep("test_steps.Steps.test_description.2", | |
331 step_class=shell.ShellCommand, | |
332 workdir="dummy", | |
333 description="single string", | |
334 descriptionDone="another string") | |
335 self.failUnlessEqual(s.description, ["single string"]) | |
336 self.failUnlessEqual(s.descriptionDone, ["another string"]) | |
337 | |
338 class VersionCheckingStep(buildstep.BuildStep): | |
339 def start(self): | |
340 # give our test a chance to run. It is non-trivial for a buildstep to | |
341 # claw its way back out to the test case which is currently running. | |
342 master = self.build.builder.botmaster.parent | |
343 checker = master._checker | |
344 checker(self) | |
345 # then complete | |
346 self.finished(buildstep.SUCCESS) | |
347 | |
348 version_config = """ | |
349 from buildbot.process import factory | |
350 from buildbot.test.test_steps import VersionCheckingStep | |
351 from buildbot.buildslave import BuildSlave | |
352 from buildbot.config import BuilderConfig | |
353 BuildmasterConfig = c = {} | |
354 f1 = factory.BuildFactory([ | |
355 factory.s(VersionCheckingStep), | |
356 ]) | |
357 c['slaves'] = [BuildSlave('bot1', 'sekrit')] | |
358 c['schedulers'] = [] | |
359 c['builders'] = [ | |
360 BuilderConfig(name='quick', slavename='bot1', factory=f1, | |
361 builddir='quickdir', slavebuilddir='quickslavedir'), | |
362 ] | |
363 c['slavePortnum'] = 0 | |
364 """ | |
365 | |
366 class SlaveVersion(RunMixin, unittest.TestCase): | |
367 def setUp(self): | |
368 RunMixin.setUp(self) | |
369 self.master.loadConfig(version_config) | |
370 self.master.startService() | |
371 d = self.connectSlave(["quick"]) | |
372 return d | |
373 | |
374 def doBuild(self, buildername): | |
375 br = base.BuildRequest("forced", SourceStamp(), 'test_builder') | |
376 d = br.waitUntilFinished() | |
377 self.control.getBuilder(buildername).requestBuild(br) | |
378 return d | |
379 | |
380 | |
381 def checkCompare(self, s): | |
382 cver = commands.command_version | |
383 v = s.slaveVersion("svn", None) | |
384 # this insures that we are getting the version correctly | |
385 self.failUnlessEqual(s.slaveVersion("svn", None), cver) | |
386 # and that non-existent commands do not provide a version | |
387 self.failUnlessEqual(s.slaveVersion("NOSUCHCOMMAND"), None) | |
388 # TODO: verify that a <=0.5.0 buildslave (which does not implement | |
389 # remote_getCommands) handles oldversion= properly. This requires a | |
390 # mutant slave which does not offer that method. | |
391 #self.failUnlessEqual(s.slaveVersion("NOSUCHCOMMAND", "old"), "old") | |
392 | |
393 # now check the comparison functions | |
394 self.failIf(s.slaveVersionIsOlderThan("svn", cver)) | |
395 self.failIf(s.slaveVersionIsOlderThan("svn", "1.1")) | |
396 self.failUnless(s.slaveVersionIsOlderThan("svn", cver + ".1")) | |
397 | |
398 self.failUnlessEqual(s.getSlaveName(), "bot1") | |
399 | |
400 def testCompare(self): | |
401 self.master._checker = self.checkCompare | |
402 d = self.doBuild("quick") | |
403 return d | |
404 | |
405 | |
406 class _SimpleBuildStep(buildstep.BuildStep): | |
407 def start(self): | |
408 args = {"arg1": "value"} | |
409 cmd = buildstep.RemoteCommand("simple", args) | |
410 d = self.runCommand(cmd) | |
411 d.addCallback(lambda res: self.finished(SUCCESS)) | |
412 | |
413 class _SimpleCommand(commands.Command): | |
414 def start(self): | |
415 self.builder.flag = True | |
416 self.builder.flag_args = self.args | |
417 return defer.succeed(None) | |
418 | |
419 class CheckStepTester(StepTester, unittest.TestCase): | |
420 def testSimple(self): | |
421 self.slavebase = "testSimple.slave" | |
422 self.masterbase = "testSimple.master" | |
423 sb = self.makeSlaveBuilder() | |
424 sb.flag = False | |
425 registry.registerSlaveCommand("simple", _SimpleCommand, "1") | |
426 step = self.makeStep(_SimpleBuildStep) | |
427 d = self.runStep(step) | |
428 def _checkSimple(results): | |
429 self.failUnless(sb.flag) | |
430 self.failUnlessEqual(sb.flag_args, {"arg1": "value"}) | |
431 d.addCallback(_checkSimple) | |
432 return d | |
433 | |
434 class Python(StepTester, unittest.TestCase): | |
435 def testPyFlakes1(self): | |
436 self.masterbase = "Python.testPyFlakes1" | |
437 step = self.makeStep(python.PyFlakes) | |
438 output = \ | |
439 """pyflakes buildbot | |
440 buildbot/changes/freshcvsmail.py:5: 'FCMaildirSource' imported but unused | |
441 buildbot/clients/debug.py:9: redefinition of unused 'gtk' from line 9 | |
442 buildbot/clients/debug.py:9: 'gnome' imported but unused | |
443 buildbot/scripts/runner.py:323: redefinition of unused 'run' from line 321 | |
444 buildbot/scripts/runner.py:325: redefinition of unused 'run' from line 323 | |
445 buildbot/scripts/imaginary.py:12: undefined name 'size' | |
446 buildbot/scripts/imaginary.py:18: 'from buildbot import *' used; unable to detec
t undefined names | |
447 """ | |
448 log = step.addLog("stdio") | |
449 log.addStdout(output) | |
450 log.finish() | |
451 step.createSummary(log) | |
452 desc = step.descriptionDone | |
453 self.failUnless("unused=2" in desc) | |
454 self.failUnless("undefined=1" in desc) | |
455 self.failUnless("redefs=3" in desc) | |
456 self.failUnless("import*=1" in desc) | |
457 self.failIf("misc=" in desc) | |
458 | |
459 self.failUnlessEqual(step.getProperty("pyflakes-unused"), 2) | |
460 self.failUnlessEqual(step.getProperty("pyflakes-undefined"), 1) | |
461 self.failUnlessEqual(step.getProperty("pyflakes-redefs"), 3) | |
462 self.failUnlessEqual(step.getProperty("pyflakes-import*"), 1) | |
463 self.failUnlessEqual(step.getProperty("pyflakes-misc"), 0) | |
464 self.failUnlessEqual(step.getProperty("pyflakes-total"), 7) | |
465 | |
466 logs = {} | |
467 for log in step.step_status.getLogs(): | |
468 logs[log.getName()] = log | |
469 | |
470 for name in ["unused", "undefined", "redefs", "import*"]: | |
471 self.failUnless(name in logs) | |
472 self.failIf("misc" in logs) | |
473 lines = logs["unused"].readlines() | |
474 self.failUnlessEqual(len(lines), 2) | |
475 self.failUnlessEqual(lines[0], "buildbot/changes/freshcvsmail.py:5: 'FCM
aildirSource' imported but unused\n") | |
476 | |
477 cmd = buildstep.RemoteCommand(None, {}) | |
478 cmd.rc = 0 | |
479 results = step.evaluateCommand(cmd) | |
480 self.failUnlessEqual(results, FAILURE) # because of the 'undefined' | |
481 | |
482 def testPyFlakes2(self): | |
483 self.masterbase = "Python.testPyFlakes2" | |
484 step = self.makeStep(python.PyFlakes) | |
485 output = \ | |
486 """pyflakes buildbot | |
487 some more text here that should be ignored | |
488 buildbot/changes/freshcvsmail.py:5: 'FCMaildirSource' imported but unused | |
489 buildbot/clients/debug.py:9: redefinition of unused 'gtk' from line 9 | |
490 buildbot/clients/debug.py:9: 'gnome' imported but unused | |
491 buildbot/scripts/runner.py:323: redefinition of unused 'run' from line 321 | |
492 buildbot/scripts/runner.py:325: redefinition of unused 'run' from line 323 | |
493 buildbot/scripts/imaginary.py:12: undefined name 'size' | |
494 could not compile 'blah/blah.py':3: | |
495 pretend there was an invalid line here | |
496 buildbot/scripts/imaginary.py:18: 'from buildbot import *' used; unable to detec
t undefined names | |
497 """ | |
498 log = step.addLog("stdio") | |
499 log.addStdout(output) | |
500 log.finish() | |
501 step.createSummary(log) | |
502 desc = step.descriptionDone | |
503 self.failUnless("unused=2" in desc) | |
504 self.failUnless("undefined=1" in desc) | |
505 self.failUnless("redefs=3" in desc) | |
506 self.failUnless("import*=1" in desc) | |
507 self.failUnless("misc=2" in desc) | |
508 | |
509 | |
510 def testPyFlakes3(self): | |
511 self.masterbase = "Python.testPyFlakes3" | |
512 step = self.makeStep(python.PyFlakes) | |
513 output = \ | |
514 """buildbot/changes/freshcvsmail.py:5: 'FCMaildirSource' imported but unused | |
515 buildbot/clients/debug.py:9: redefinition of unused 'gtk' from line 9 | |
516 buildbot/clients/debug.py:9: 'gnome' imported but unused | |
517 buildbot/scripts/runner.py:323: redefinition of unused 'run' from line 321 | |
518 buildbot/scripts/runner.py:325: redefinition of unused 'run' from line 323 | |
519 buildbot/scripts/imaginary.py:12: undefined name 'size' | |
520 buildbot/scripts/imaginary.py:18: 'from buildbot import *' used; unable to detec
t undefined names | |
521 """ | |
522 log = step.addLog("stdio") | |
523 log.addStdout(output) | |
524 log.finish() | |
525 step.createSummary(log) | |
526 desc = step.descriptionDone | |
527 self.failUnless("unused=2" in desc) | |
528 self.failUnless("undefined=1" in desc) | |
529 self.failUnless("redefs=3" in desc) | |
530 self.failUnless("import*=1" in desc) | |
531 self.failIf("misc" in desc) | |
532 | |
533 | |
534 class OrdinaryCompile(shell.Compile): | |
535 warningPattern = "ordinary line" | |
536 | |
537 class Warnings(StepTester, unittest.TestCase): | |
538 def testCompile1(self): | |
539 self.masterbase = "Warnings.testCompile1" | |
540 step = self.makeStep(shell.Compile) | |
541 output = \ | |
542 """Compile started | |
543 normal line | |
544 warning: oh noes! | |
545 ordinary line | |
546 error (but we aren't looking for errors now, are we) | |
547 line 23: warning: we are now on line 23 | |
548 ending line | |
549 """ | |
550 log = step.addLog("stdio") | |
551 log.addStdout(output) | |
552 log.finish() | |
553 step.createSummary(log) | |
554 self.failUnlessEqual(step.getProperty("warnings-count"), 2) | |
555 logs = {} | |
556 for log in step.step_status.getLogs(): | |
557 logs[log.getName()] = log | |
558 self.failUnless("warnings" in logs) | |
559 lines = logs["warnings"].readlines() | |
560 self.failUnlessEqual(len(lines), 2) | |
561 self.failUnlessEqual(lines[0], "warning: oh noes!\n") | |
562 self.failUnlessEqual(lines[1], | |
563 "line 23: warning: we are now on line 23\n") | |
564 | |
565 cmd = buildstep.RemoteCommand(None, {}) | |
566 cmd.rc = 0 | |
567 results = step.evaluateCommand(cmd) | |
568 self.failUnlessEqual(results, WARNINGS) | |
569 | |
570 def testCompile2(self): | |
571 self.masterbase = "Warnings.testCompile2" | |
572 step = self.makeStep(shell.Compile, warningPattern="ordinary line") | |
573 output = \ | |
574 """Compile started | |
575 normal line | |
576 warning: oh noes! | |
577 ordinary line | |
578 error (but we aren't looking for errors now, are we) | |
579 line 23: warning: we are now on line 23 | |
580 ending line | |
581 """ | |
582 log = step.addLog("stdio") | |
583 log.addStdout(output) | |
584 log.finish() | |
585 step.createSummary(log) | |
586 self.failUnlessEqual(step.getProperty("warnings-count"), 1) | |
587 logs = {} | |
588 for log in step.step_status.getLogs(): | |
589 logs[log.getName()] = log | |
590 self.failUnless("warnings" in logs) | |
591 lines = logs["warnings"].readlines() | |
592 self.failUnlessEqual(len(lines), 1) | |
593 self.failUnlessEqual(lines[0], "ordinary line\n") | |
594 | |
595 cmd = buildstep.RemoteCommand(None, {}) | |
596 cmd.rc = 0 | |
597 results = step.evaluateCommand(cmd) | |
598 self.failUnlessEqual(results, WARNINGS) | |
599 | |
600 def testCompile3(self): | |
601 self.masterbase = "Warnings.testCompile3" | |
602 step = self.makeStep(OrdinaryCompile) | |
603 output = \ | |
604 """Compile started | |
605 normal line | |
606 warning: oh noes! | |
607 ordinary line | |
608 error (but we aren't looking for errors now, are we) | |
609 line 23: warning: we are now on line 23 | |
610 ending line | |
611 """ | |
612 step.setProperty("warnings-count", 10, "test") | |
613 log = step.addLog("stdio") | |
614 log.addStdout(output) | |
615 log.finish() | |
616 step.createSummary(log) | |
617 self.failUnlessEqual(step.getProperty("warnings-count"), 11) | |
618 logs = {} | |
619 for log in step.step_status.getLogs(): | |
620 logs[log.getName()] = log | |
621 self.failUnless("warnings" in logs) | |
622 lines = logs["warnings"].readlines() | |
623 self.failUnlessEqual(len(lines), 1) | |
624 self.failUnlessEqual(lines[0], "ordinary line\n") | |
625 | |
626 cmd = buildstep.RemoteCommand(None, {}) | |
627 cmd.rc = 0 | |
628 results = step.evaluateCommand(cmd) | |
629 self.failUnlessEqual(results, WARNINGS) | |
630 | |
631 def testCompile4(self): | |
632 # Test suppression of warnings. | |
633 self.masterbase = "Warnings.testCompile4" | |
634 step = self.makeStep(shell.Compile, | |
635 warningPattern="^(.*?):([0-9]+): [Ww]arning: (.*)$"
, | |
636 warningExtractor=shell.Compile.warnExtractFromRegex
pGroups, | |
637 directoryEnterPattern="make.*: Entering directory [
\"`'](.*)['`\"]", | |
638 directoryLeavePattern="make.*: Leaving directory") | |
639 step.addSuppression([(r"/subdir/", r"xyzzy", None, None), | |
640 (r"foo.c", r".*", None, 20), | |
641 (r"foo.c", r".*", 200, None), | |
642 (r"foo.c", r".*", 50, 50), | |
643 (r"xxx", r".*", None, None), | |
644 ]) | |
645 log = step.addLog("stdio") | |
646 output = \ | |
647 """Making all in . | |
648 make[1]: Entering directory `/abs/path/build' | |
649 foo.c:10: warning: `bar' defined but not used | |
650 foo.c:50: warning: `bar' defined but not used | |
651 make[2]: Entering directory `/abs/path/build/subdir' | |
652 baz.c:33: warning: `xyzzy' defined but not used | |
653 baz.c:34: warning: `magic' defined but not used | |
654 make[2]: Leaving directory `/abs/path/build/subdir' | |
655 foo.c:100: warning: `xyzzy' defined but not used | |
656 foo.c:200: warning: `bar' defined but not used | |
657 make[2]: Leaving directory `/abs/path/build' | |
658 """ | |
659 log.addStdout(output) | |
660 log.finish() | |
661 step.createSummary(log) | |
662 self.failUnlessEqual(step.getProperty("warnings-count"), 2) | |
663 logs = {} | |
664 for log in step.step_status.getLogs(): | |
665 logs[log.getName()] = log | |
666 self.failUnless("warnings" in logs) | |
667 lines = logs["warnings"].readlines() | |
668 self.failUnlessEqual(len(lines), 2) | |
669 self.failUnlessEqual(lines[0], "baz.c:34: warning: `magic' defined but n
ot used\n") | |
670 self.failUnlessEqual(lines[1], "foo.c:100: warning: `xyzzy' defined but
not used\n") | |
671 | |
672 cmd = buildstep.RemoteCommand(None, {}) | |
673 cmd.rc = 0 | |
674 results = step.evaluateCommand(cmd) | |
675 self.failUnlessEqual(results, WARNINGS) | |
676 | |
677 def filterArgs(self, args): | |
678 if "writer" in args: | |
679 args["writer"] = self.wrap(args["writer"]) | |
680 return args | |
681 | |
682 suppressionFileData = """ | |
683 # Sample suppressions file for testing | |
684 | |
685 /subdir/ : xyzzy | |
686 foo.c: .* : 0-20 | |
687 foo.c: .*: 200-10000 | |
688 foo.c :.*: 50 | |
689 xxx : .* | |
690 """ | |
691 def testCompile5(self): | |
692 # Test downloading warning suppression file from slave. | |
693 self.slavebase = "Warnings.testCompile5.slave" | |
694 self.masterbase = "Warnings.testCompile5.master" | |
695 sb = self.makeSlaveBuilder() | |
696 os.mkdir(os.path.join(self.slavebase, self.slavebuilderbase, | |
697 "build")) | |
698 output = \ | |
699 """Making all in . | |
700 make[1]: Entering directory `/abs/path/build' | |
701 foo.c:10: warning: `bar' defined but not used | |
702 foo.c:50: warning: `bar' defined but not used | |
703 make[2]: Entering directory `/abs/path/build/subdir' | |
704 baz.c:33: warning: `xyzzy' defined but not used | |
705 baz.c:34: warning: `magic' defined but not used | |
706 make[2]: Leaving directory `/abs/path/build/subdir' | |
707 foo.c:100: warning: `xyzzy' defined but not used | |
708 foo.c:200: warning: `bar' defined but not used | |
709 make[2]: Leaving directory `/abs/path/build' | |
710 """ | |
711 printStatement = ('print """%s"""' % output) | |
712 step = self.makeStep(shell.Compile, | |
713 warningPattern="^(.*?):([0-9]+): [Ww]arning: (.*)$"
, | |
714 warningExtractor=shell.Compile.warnExtractFromRegex
pGroups, | |
715 suppressionFile="warnings.supp", | |
716 command=[sys.executable, "-c", printStatement]) | |
717 self.failUnlessEqual(step.step_status.getText(), ['compiling']) | |
718 slavesrc = os.path.join(self.slavebase, | |
719 self.slavebuilderbase, | |
720 "build", | |
721 "warnings.supp") | |
722 open(slavesrc, "w").write(self.suppressionFileData) | |
723 | |
724 d = self.runStep(step) | |
725 def _checkResult(result): | |
726 self.failUnlessEqual(step.getProperty("warnings-count"), 2) | |
727 logs = {} | |
728 for log in step.step_status.getLogs(): | |
729 logs[log.getName()] = log | |
730 self.failUnless("warnings" in logs) | |
731 lines = logs["warnings"].readlines() | |
732 self.failUnlessEqual(len(lines), 2) | |
733 self.failUnlessEqual(lines[0], "baz.c:34: warning: `magic' defined b
ut not used\n") | |
734 self.failUnlessEqual(lines[1], "foo.c:100: warning: `xyzzy' defined
but not used\n") | |
735 self.failUnlessEqual(step.step_status.getText(), ['compile', | |
736 'warnings']) | |
737 | |
738 d.addCallback(_checkResult) | |
739 return d | |
740 | |
741 class TreeSize(StepTester, unittest.TestCase): | |
742 def testTreeSize(self): | |
743 self.slavebase = "TreeSize.testTreeSize.slave" | |
744 self.masterbase = "TreeSize.testTreeSize.master" | |
745 | |
746 sb = self.makeSlaveBuilder() | |
747 step = self.makeStep(shell.TreeSize) | |
748 d = self.runStep(step) | |
749 def _check(results): | |
750 self.failUnlessEqual(results, SUCCESS) | |
751 kib = step.getProperty("tree-size-KiB") | |
752 self.failUnless(isinstance(kib, int)) | |
753 self.failUnless(kib < 100) # should be empty, I get '4' | |
754 s = step.step_status | |
755 self.failUnlessEqual(" ".join(s.getText()), | |
756 "treesize %d KiB" % kib) | |
757 d.addCallback(_check) | |
758 return d | |
759 | |
760 class FakeCommand: | |
761 def __init__(self, rc): | |
762 self.rc = rc | |
763 | |
764 class PerlModuleTest(StepTester, unittest.TestCase): | |
765 def testAllTestsPassed(self): | |
766 self.masterbase = "PMT.testAllTestsPassed" | |
767 step = self.makeStep(shell.PerlModuleTest) | |
768 output = \ | |
769 """ok 1 | |
770 ok 2 | |
771 All tests successful | |
772 Files=1, Tests=123, other stuff | |
773 """ | |
774 log = step.addLog("stdio") | |
775 log.addStdout(output) | |
776 log.finish() | |
777 rc = step.evaluateCommand(FakeCommand(rc=241)) | |
778 self.failUnlessEqual(rc, SUCCESS) | |
779 ss = step.step_status | |
780 self.failUnlessEqual(ss.getStatistic('tests-failed'), 0) | |
781 self.failUnlessEqual(ss.getStatistic('tests-total'), 123) | |
782 self.failUnlessEqual(ss.getStatistic('tests-passed'), 123) | |
783 | |
784 def testFailures_OldTestHarness(self): | |
785 self.masterbase = "PMT.testFailures_OldTestHarness" | |
786 step = self.makeStep(shell.PerlModuleTest) | |
787 output = \ | |
788 """ | |
789 ok 1 | |
790 ok 2 | |
791 3/7 subtests failed | |
792 """ | |
793 log = step.addLog("stdio") | |
794 log.addStdout(output) | |
795 log.finish() | |
796 rc = step.evaluateCommand(FakeCommand(rc = 123)) | |
797 self.failUnlessEqual(rc, FAILURE) | |
798 ss = step.step_status | |
799 self.failUnlessEqual(ss.getStatistic('tests-failed'), 3) | |
800 self.failUnlessEqual(ss.getStatistic('tests-total'), 7) | |
801 self.failUnlessEqual(ss.getStatistic('tests-passed'), 4) | |
802 | |
803 def testFailures_UnparseableStdio(self): | |
804 self.masterbase = "PMT.testFailures_UnparseableStdio" | |
805 step = self.makeStep(shell.PerlModuleTest) | |
806 output = \ | |
807 """ | |
808 just some random stuff, you know | |
809 """ | |
810 log = step.addLog("stdio") | |
811 log.addStdout(output) | |
812 log.finish() | |
813 rc = step.evaluateCommand(FakeCommand(rc = 243)) | |
814 self.failUnlessEqual(rc, 243) | |
815 ss = step.step_status | |
816 self.failUnlessEqual(ss.getStatistic('tests-failed'), None) | |
817 self.failUnlessEqual(ss.getStatistic('tests-total'), None) | |
818 self.failUnlessEqual(ss.getStatistic('tests-passed'), None) | |
819 | |
820 def testFailures_NewTestHarness(self): | |
821 self.masterbase = "PMT.testFailures_NewTestHarness" | |
822 step = self.makeStep(shell.PerlModuleTest) | |
823 output = \ | |
824 """ | |
825 # Looks like you failed 15 tests of 18. | |
826 tests/services.......................... Failed 265/30904 subtests | |
827 (less 16 skipped subtests: 30623 okay) | |
828 tests/simple_query_backend..............ok | |
829 tests/simple_query_middleware...........ok | |
830 tests/soap_globalcollect................ok | |
831 tests/three_d_me........................ok | |
832 tests/three_d_me_callback...............ok | |
833 tests/transaction_create................ok | |
834 tests/unique_txid.......................ok | |
835 | |
836 Test Summary Report | |
837 ------------------- | |
838 tests/000policies (Wstat: 5632 Tests: 9078 Failed: 22) | |
839 Failed tests: 2409, 2896-2897, 2900-2901, 2940-2941, 2944-2945 | |
840 2961-2962, 2965-2966, 2969-2970, 2997-2998 | |
841 3262, 3281-3282, 3288-3289 | |
842 Non-zero exit status: 22 | |
843 tests/services (Wstat: 0 Tests: 30904 Failed: 265) | |
844 Failed tests: 14, 16-21, 64-69, 71-96, 98, 30157, 30159 | |
845 30310, 30316, 30439-30543, 30564, 30566-30577 | |
846 30602, 30604-30607, 30609-30612, 30655 | |
847 30657-30668, 30675, 30697-30716, 30718-30720 | |
848 30722-30736, 30773-30774, 30776-30777, 30786 | |
849 30791, 30795, 30797, 30801, 30822-30827 | |
850 30830-30831, 30848-30855, 30858-30859, 30888-30899 | |
851 30901, 30903-30904 | |
852 Files=68, Tests=264809, 1944 wallclock secs (17.59 usr 0.63 sys + 470.04 cusr 1
31.40 csys = 619.66 CPU) | |
853 Result: FAIL | |
854 """ | |
855 log = step.addLog("stdio") | |
856 log.addStdout(output) | |
857 log.finish() | |
858 rc = step.evaluateCommand(FakeCommand(rc=87)) | |
859 self.failUnlessEqual(rc, FAILURE) | |
860 ss = step.step_status | |
861 self.failUnlessEqual(ss.getStatistic('tests-failed'), 287) | |
862 self.failUnlessEqual(ss.getStatistic('tests-total'), 264809) | |
863 self.failUnlessEqual(ss.getStatistic('tests-passed'), 264522) | |
864 | |
865 class MasterShellCommand(StepTester, unittest.TestCase): | |
866 def testMasterShellCommand(self): | |
867 self.slavebase = "testMasterShellCommand.slave" | |
868 self.masterbase = "testMasterShellCommand.master" | |
869 sb = self.makeSlaveBuilder() | |
870 step = self.makeStep(master.MasterShellCommand, command=['echo', | |
871 WithProperties("hi build-%(other)s.tar.gz")]) | |
872 step.build.setProperty("other", "foo", "test") | |
873 | |
874 # we can't invoke runStep until the reactor is started .. hence this | |
875 # little dance | |
876 d = defer.Deferred() | |
877 def _dotest(_): | |
878 return self.runStep(step) | |
879 d.addCallback(_dotest) | |
880 | |
881 def _check(results): | |
882 self.failUnlessEqual(results, SUCCESS) | |
883 logtxt = step.getLog("stdio").getText() | |
884 self.failUnlessEqual(logtxt.strip(), "hi build-foo.tar.gz") | |
885 d.addCallback(_check) | |
886 reactor.callLater(0, d.callback, None) | |
887 return d | |
888 | |
889 def testMasterShellCommand_badexit(self): | |
890 self.slavebase = "testMasterShellCommand_badexit.slave" | |
891 self.masterbase = "testMasterShellCommand_badexit.master" | |
892 sb = self.makeSlaveBuilder() | |
893 step = self.makeStep(master.MasterShellCommand, command="exit 1") | |
894 | |
895 # we can't invoke runStep until the reactor is started .. hence this | |
896 # little dance | |
897 d = defer.Deferred() | |
898 def _dotest(_): | |
899 return self.runStep(step) | |
900 d.addCallback(_dotest) | |
901 | |
902 def _check(results): | |
903 self.failUnlessEqual(results, FAILURE) | |
904 d.addCallback(_check) | |
905 reactor.callLater(0, d.callback, None) | |
906 return d | |
907 | |
908 class SuccessStep(buildstep.BuildStep): | |
909 def start(self): | |
910 self.finished(buildstep.SUCCESS) | |
911 | |
912 class ConditionalStepTest(StepTester, unittest.TestCase): | |
913 def testNotSkipped(self): | |
914 self.slavebase = "testNotSkipped.slave" | |
915 self.masterbase = "testNotSkipped.master" | |
916 sb = self.makeSlaveBuilder() | |
917 step = self.makeStep(SuccessStep) | |
918 d = self.runStep(step) | |
919 def _checkResults(results): | |
920 self.failUnlessEqual(SUCCESS, results) | |
921 d.addCallback(_checkResults) | |
922 return d | |
923 | |
924 def testSkipped(self): | |
925 self.slavebase = "testSkipped.slave" | |
926 self.masterbase = "testSkipped.master" | |
927 sb = self.makeSlaveBuilder() | |
928 step = self.makeStep(SuccessStep, doStepIf=False) | |
929 d = self.runStep(step) | |
930 def _checkResults(results): | |
931 self.failUnlessEqual(SKIPPED, results) | |
932 d.addCallback(_checkResults) | |
933 return d | |
934 | |
935 def testNotSkippedFunc(self): | |
936 self.slavebase = "testNotSkippedFunc.slave" | |
937 self.masterbase = "testNotSkippedFunc.master" | |
938 sb = self.makeSlaveBuilder() | |
939 step = self.makeStep(SuccessStep, doStepIf=lambda s: True) | |
940 d = self.runStep(step) | |
941 def _checkResults(results): | |
942 self.failUnlessEqual(SUCCESS, results) | |
943 d.addCallback(_checkResults) | |
944 return d | |
945 | |
946 def testSkippedFunc(self): | |
947 self.slavebase = "testSkippedFunc.slave" | |
948 self.masterbase = "testSkippedFunc.master" | |
949 sb = self.makeSlaveBuilder() | |
950 step = self.makeStep(SuccessStep, doStepIf=lambda s: False) | |
951 d = self.runStep(step) | |
952 def _checkResults(results): | |
953 self.failUnlessEqual(SKIPPED, results) | |
954 d.addCallback(_checkResults) | |
955 return d | |
OLD | NEW |