Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(305)

Side by Side Diff: third_party/buildbot_7_12/buildbot/test/test_vc.py

Issue 12207158: Bye bye buildbot 0.7.12. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: buildbot.test.test_vc -*-
2 # -*- coding: utf-8 -*-
3
4 import sys, os, time, re
5 from email.Utils import mktime_tz, parsedate_tz
6
7 from twisted.trial import unittest
8 from twisted.internet import defer, reactor, utils, protocol, task, error
9 from twisted.python import failure
10 from twisted.python.procutils import which
11 from twisted.web import client, static, server
12
13 #defer.Deferred.debug = True
14
15 from twisted.python import log
16 #log.startLogging(sys.stderr)
17
18 from buildbot import master, interfaces
19 from buildbot.slave import bot, commands
20 from buildbot.slave.commands import rmdirRecursive
21 from buildbot.status.builder import SUCCESS, FAILURE
22 from buildbot.process import base
23 from buildbot.steps import source
24 from buildbot.changes import changes
25 from buildbot.sourcestamp import SourceStamp
26 from buildbot.scripts import tryclient
27 from buildbot.test.runutils import SignalMixin, myGetProcessOutputAndValue
28
29 #step.LoggedRemoteCommand.debug = True
30
31 from twisted.internet.defer import waitForDeferred, deferredGenerator
32
33 # Most of these tests (all but SourceStamp) depend upon having a set of
34 # repositories from which we can perform checkouts. These repositories are
35 # created by the setUp method at the start of each test class. In earlier
36 # versions these repositories were created offline and distributed with a
37 # separate tarball named 'buildbot-test-vc-1.tar.gz'. This is no longer
38 # necessary.
39
40 # CVS requires a local file repository. Providing remote access is beyond
41 # the feasible abilities of this test program (needs pserver or ssh).
42
43 # SVN requires a local file repository. To provide remote access over HTTP
44 # requires an apache server with DAV support and mod_svn, way beyond what we
45 # can test from here.
46
47 # Arch and Darcs both allow remote (read-only) operation with any web
48 # server. We test both local file access and HTTP access (by spawning a
49 # small web server to provide access to the repository files while the test
50 # is running).
51
52 # Perforce starts the daemon running on localhost. Unfortunately, it must
53 # use a predetermined Internet-domain port number, unless we want to go
54 # all-out: bind the listen socket ourselves and pretend to be inetd.
55
56 config_vc = """
57 from buildbot.process import factory
58 from buildbot.steps import source
59 from buildbot.buildslave import BuildSlave
60 from buildbot.config import BuilderConfig
61 s = factory.s
62
63 f1 = factory.BuildFactory([
64 %s,
65 ])
66 c = {}
67 c['slaves'] = [BuildSlave('bot1', 'sekrit')]
68 c['schedulers'] = []
69 c['builders'] = [
70 BuilderConfig(name='vç', slavename='bot1', factory=f1, builddir='vc-dir'),
71 ]
72 c['slavePortnum'] = 0
73 # do not compress logs in tests
74 c['logCompressionLimit'] = False
75 BuildmasterConfig = c
76 """
77
78 p0_diff = r"""
79 Index: subdir/subdir.c
80 ===================================================================
81 RCS file: /home/warner/stuff/Projects/BuildBot/code-arch/_trial_temp/test_vc/rep ositories/CVS-Repository/sample/subdir/subdir.c,v
82 retrieving revision 1.1.1.1
83 diff -u -r1.1.1.1 subdir.c
84 --- subdir/subdir.c 14 Aug 2005 01:32:49 -0000 1.1.1.1
85 +++ subdir/subdir.c 14 Aug 2005 01:36:15 -0000
86 @@ -4,6 +4,6 @@
87 int
88 main(int argc, const char *argv[])
89 {
90 - printf("Hello subdir.\n");
91 + printf("HellÒ patched subdir.\n");
92 return 0;
93 }
94 """
95
96 # Test buildbot try --root support.
97 subdir_diff = p0_diff.replace('subdir/subdir.c', 'subdir.c')
98
99 # Test --patchlevel support.
100 p2_diff = p0_diff.replace('subdir/subdir.c', 'foo/bar/subdir/subdir.c')
101
102 # Used in do_patch() test.
103 PATCHLEVEL0, SUBDIR_ROOT, PATCHLEVEL2 = range(3)
104
105 # this patch does not include the filename headers, so it is
106 # patchlevel-neutral
107 TRY_PATCH = '''
108 @@ -5,6 +5,6 @@
109 int
110 main(int argc, const char *argv[])
111 {
112 - printf("Hello subdir.\\n");
113 + printf("HÉllo try.\\n");
114 return 0;
115 }
116 '''
117
118 MAIN_C = '''
119 // this is main.c
120 #include <stdio.h>
121
122 int
123 main(int argc, const char *argv[])
124 {
125 printf("Hello world.\\n");
126 return 0;
127 }
128 '''
129
130 BRANCH_C = '''
131 // this is main.c
132 #include <stdio.h>
133
134 int
135 main(int argc, const char *argv[])
136 {
137 printf("Hello branch.\\n");
138 return 0;
139 }
140 '''
141
142 VERSION_C = '''
143 // this is version.c
144 #include <stdio.h>
145
146 int
147 main(int argc, const char *argv[])
148 {
149 printf("Hello world, version=%d\\n");
150 return 0;
151 }
152 '''
153
154 SUBDIR_C = '''
155 // this is subdir/subdir.c
156 #include <stdio.h>
157
158 int
159 main(int argc, const char *argv[])
160 {
161 printf("Hello subdir.\\n");
162 return 0;
163 }
164 '''
165
166 TRY_C = '''
167 // this is subdir/subdir.c
168 #include <stdio.h>
169
170 int
171 main(int argc, const char *argv[])
172 {
173 printf("Hello try.\\n");
174 return 0;
175 }
176 '''
177
178 def _makedirsif(dir):
179 absdir = os.path.abspath(dir)
180 if not os.path.isdir(absdir):
181 os.makedirs(absdir)
182
183 def qw(s):
184 return s.split()
185
186 class VCS_Helper:
187 # this is a helper class which keeps track of whether each VC system is
188 # available, and whether the repository for each has been created. There
189 # is one instance of this class, at module level, shared between all test
190 # cases.
191
192 def __init__(self):
193 self._helpers = {}
194 self._isCapable = {}
195 self._excuses = {}
196 self._repoReady = {}
197
198 def registerVC(self, name, helper):
199 self._helpers[name] = helper
200 self._repoReady[name] = False
201
202 def skipIfNotCapable(self, name):
203 """Either return None, or raise SkipTest"""
204 d = self.capable(name)
205 def _maybeSkip(res):
206 if not res[0]:
207 raise unittest.SkipTest(res[1])
208 d.addCallback(_maybeSkip)
209 return d
210
211 def capable(self, name):
212 """Return a Deferred that fires with (True,None) if this host offers
213 the given VC tool, or (False,excuse) if it does not (and therefore
214 the tests should be skipped)."""
215
216 if self._isCapable.has_key(name):
217 if self._isCapable[name]:
218 return defer.succeed((True,None))
219 else:
220 return defer.succeed((False, self._excuses[name]))
221 d = defer.maybeDeferred(self._helpers[name].capable)
222 def _capable(res):
223 if res[0]:
224 self._isCapable[name] = True
225 else:
226 self._excuses[name] = res[1]
227 return res
228 d.addCallback(_capable)
229 return d
230
231 def getHelper(self, name):
232 return self._helpers[name]
233
234 def createRepository(self, name):
235 """Return a Deferred that fires when the repository is set up."""
236 if self._repoReady[name]:
237 return defer.succeed(True)
238 d = self._helpers[name].createRepository()
239 def _ready(res):
240 self._repoReady[name] = True
241 d.addCallback(_ready)
242 return d
243
244 VCS = VCS_Helper()
245
246
247 # the overall plan here:
248 #
249 # Each VC system is tested separately, all using the same source tree defined
250 # in the 'files' dictionary above. Each VC system gets its own TestCase
251 # subclass. The first test case that is run will create the repository during
252 # setUp(), making two branches: 'trunk' and 'branch'. The trunk gets a copy
253 # of all the files in 'files'. The variant of good.c is committed on the
254 # branch.
255 #
256 # then testCheckout is run, which does a number of checkout/clobber/update
257 # builds. These all use trunk r1. It then runs self.fix(), which modifies
258 # 'fixable.c', then performs another build and makes sure the tree has been
259 # updated.
260 #
261 # testBranch uses trunk-r1 and branch-r1, making sure that we clobber the
262 # tree properly when we switch between them
263 #
264 # testPatch does a trunk-r1 checkout and applies a patch.
265 #
266 # testTryGetPatch performs a trunk-r1 checkout, modifies some files, then
267 # verifies that tryclient.getSourceStamp figures out the base revision and
268 # what got changed.
269
270
271 # vc_create makes a repository at r1 with three files: main.c, version.c, and
272 # subdir/foo.c . It also creates a branch from r1 (called b1) in which main.c
273 # says "hello branch" instead of "hello world". self.trunk[] contains
274 # revision stamps for everything on the trunk, and self.branch[] does the
275 # same for the branch.
276
277 # vc_revise() checks out a tree at HEAD, changes version.c, then checks it
278 # back in. The new version stamp is appended to self.trunk[]. The tree is
279 # removed afterwards.
280
281 # vc_try_checkout(workdir, rev) checks out a tree at REV, then changes
282 # subdir/subdir.c to say 'Hello try'
283 # vc_try_finish(workdir) removes the tree and cleans up any VC state
284 # necessary (like deleting the Arch archive entry).
285
286
287 class BaseHelper:
288 def __init__(self):
289 self.trunk = []
290 self.branch = []
291 self.allrevs = []
292
293 def capable(self):
294 # this is also responsible for setting self.vcexe
295 raise NotImplementedError
296
297 def createBasedir(self):
298 # you must call this from createRepository
299 self.repbase = os.path.abspath(os.path.join("test_vc",
300 "repositories"))
301 rmdirRecursive(self.repbase)
302 os.makedirs(self.repbase)
303
304 def createRepository(self):
305 # this will only be called once per process
306 raise NotImplementedError
307
308 def populate(self, basedir):
309 _makedirsif(basedir)
310 _makedirsif(os.path.join(basedir, "subdir"))
311
312 open(os.path.join(basedir, "main.c"), "w").write(MAIN_C)
313 self.version = 1
314 version_c = VERSION_C % self.version
315 open(os.path.join(basedir, "version.c"), "w").write(version_c)
316 open(os.path.join(basedir, "main.c"), "w").write(MAIN_C)
317 open(os.path.join(basedir, "subdir", "subdir.c"), "w").write(SUBDIR_C)
318
319 def populate_branch(self, basedir):
320 open(os.path.join(basedir, "main.c"), "w").write(BRANCH_C)
321
322 def addTrunkRev(self, rev):
323 self.trunk.append(rev)
324 self.allrevs.append(rev)
325 def addBranchRev(self, rev):
326 self.branch.append(rev)
327 self.allrevs.append(rev)
328
329 def runCommand(self, basedir, command, failureIsOk=False,
330 stdin=None, env=None):
331 # all commands passed to do() should be strings or lists. If they are
332 # strings, none of the arguments may have spaces. This makes the
333 # commands less verbose at the expense of restricting what they can
334 # specify.
335 if type(command) not in (list, tuple):
336 command = command.split(" ")
337
338 # execute scripts through cmd.exe on windows, to avoid space in path iss ues
339 if sys.platform == 'win32' and command[0].lower().endswith('.cmd'):
340 command = [which('cmd.exe')[0], '/c', 'call'] + command
341
342 DEBUG = False
343 if DEBUG:
344 print "do %s" % command
345 print " in basedir %s" % basedir
346 if stdin:
347 print " STDIN:\n", stdin, "\n--STDIN DONE"
348
349 if not env:
350 env = os.environ.copy()
351 env['LC_ALL'] = "C"
352 d = myGetProcessOutputAndValue(command[0], command[1:],
353 env=env, path=basedir,
354 stdin=stdin)
355 def check((out, err, code)):
356 if DEBUG:
357 print
358 print "command was: %s" % command
359 if out: print "out: %s" % out
360 if err: print "err: %s" % err
361 print "code: %s" % code
362 if code != 0 and not failureIsOk:
363 log.msg("command %s finished with exit code %d" %
364 (command, code))
365 log.msg(" and stdout %s" % (out,))
366 log.msg(" and stderr %s" % (err,))
367 raise RuntimeError("command %s finished with exit code %d"
368 % (command, code)
369 + ": see logs for stdout")
370 return out
371 d.addCallback(check)
372 return d
373
374 def do(self, basedir, command, failureIsOk=False, stdin=None, env=None):
375 d = self.runCommand(basedir, command, failureIsOk=failureIsOk,
376 stdin=stdin, env=env)
377 return waitForDeferred(d)
378
379 def dovc(self, basedir, command, failureIsOk=False, stdin=None, env=None):
380 """Like do(), but the VC binary will be prepended to COMMAND."""
381 if isinstance(command, (str, unicode)):
382 command = [self.vcexe] + command.split(' ')
383 else:
384 # command is a list
385 command = [self.vcexe] + command
386 return self.do(basedir, command, failureIsOk, stdin, env)
387
388 class VCBase(SignalMixin):
389 metadir = None
390 createdRepository = False
391 master = None
392 slave = None
393 helper = None
394 httpServer = None
395 httpPort = None
396 skip = None
397 has_got_revision = False
398 has_got_revision_branches_are_merged = False # for SVN
399
400 def failUnlessIn(self, substring, string, msg=None):
401 # trial provides a version of this that requires python-2.3 to test
402 # strings.
403 if msg is None:
404 msg = ("did not see the expected substring '%s' in string '%s'" %
405 (substring, string))
406 self.failUnless(string.find(substring) != -1, msg)
407
408 def setUp(self):
409 self.setUpSignalHandler()
410 d = VCS.skipIfNotCapable(self.vc_name)
411 d.addCallback(self._setUp1)
412 return d
413
414 def _setUp1(self, res):
415 self.helper = VCS.getHelper(self.vc_name)
416
417 if os.path.exists("basedir"):
418 rmdirRecursive("basedir")
419 os.mkdir("basedir")
420 self.master = master.BuildMaster("basedir")
421 self.slavebase = os.path.abspath("slavebase")
422 if os.path.exists(self.slavebase):
423 rmdirRecursive(self.slavebase)
424 os.mkdir("slavebase")
425
426 d = VCS.createRepository(self.vc_name)
427 return d
428
429 def connectSlave(self):
430 port = self.master.slavePort._port.getHost().port
431 slave = bot.BuildSlave("localhost", port, "bot1", "sekrit",
432 self.slavebase, keepalive=0, usePTY=False)
433 self.slave = slave
434 slave.startService()
435 d = self.master.botmaster.waitUntilBuilderAttached("vç")
436 return d
437
438 def loadConfig(self, config):
439 # reloading the config file causes a new 'listDirs' command to be
440 # sent to the slave. To synchronize on this properly, it is easiest
441 # to stop and restart the slave.
442 d = defer.succeed(None)
443 if self.slave:
444 d = self.master.botmaster.waitUntilBuilderDetached("vç")
445 self.slave.stopService()
446 d.addCallback(lambda res: self.master.loadConfig(config))
447 d.addCallback(lambda res: self.connectSlave())
448 return d
449
450 def serveHTTP(self):
451 # launch an HTTP server to serve the repository files
452 self.root = static.File(self.helper.repbase)
453 self.site = server.Site(self.root)
454 self.httpServer = reactor.listenTCP(0, self.site)
455 self.httpPort = self.httpServer.getHost().port
456
457 def doBuild(self, shouldSucceed=True, ss=None):
458 c = interfaces.IControl(self.master)
459
460 if ss is None:
461 ss = SourceStamp()
462 #print "doBuild(ss: b=%s rev=%s)" % (ss.branch, ss.revision)
463 req = base.BuildRequest("test_vc forced build", ss, 'test_builder')
464 d = req.waitUntilFinished()
465 c.getBuilder("vç").requestBuild(req)
466 d.addCallback(self._doBuild_1, shouldSucceed)
467 return d
468 def _doBuild_1(self, bs, shouldSucceed):
469 r = bs.getResults()
470 if r != SUCCESS and shouldSucceed:
471 print
472 print
473 if not bs.isFinished():
474 print "Hey, build wasn't even finished!"
475 print "Build did not succeed:", r, bs.getText()
476 for s in bs.getSteps():
477 for l in s.getLogs():
478 print "--- START step %s / log %s ---" % (s.getName(),
479 l.getName())
480 print l.getTextWithHeaders()
481 print "--- STOP ---"
482 print
483 self.fail("build did not succeed")
484 return bs
485
486 def printLogs(self, bs):
487 for s in bs.getSteps():
488 for l in s.getLogs():
489 print "--- START step %s / log %s ---" % (s.getName(),
490 l.getName())
491 print l.getTextWithHeaders()
492 print "--- STOP ---"
493 print
494
495 def touch(self, d, f):
496 open(os.path.join(d,f),"w").close()
497 def shouldExist(self, *args):
498 target = os.path.join(*args)
499 self.failUnless(os.path.exists(target),
500 "expected to find %s but didn't" % target)
501 def shouldNotExist(self, *args):
502 target = os.path.join(*args)
503 self.failIf(os.path.exists(target),
504 "expected to NOT find %s, but did" % target)
505 def shouldContain(self, d, f, contents):
506 c = open(os.path.join(d, f), "r").read()
507 self.failUnlessIn(contents, c)
508
509 def checkGotRevision(self, bs, expected):
510 if self.has_got_revision:
511 self.failUnlessEqual(bs.getProperty("got_revision"), str(expected))
512
513 def checkGotRevisionIsLatest(self, bs):
514 expected = self.helper.trunk[-1]
515 if self.has_got_revision_branches_are_merged:
516 expected = self.helper.allrevs[-1]
517 self.checkGotRevision(bs, expected)
518
519 def do_vctest(self, testRetry=True):
520 vctype = self.vctype
521 args = self.helper.vcargs
522 m = self.master
523 self.vcdir = os.path.join(self.slavebase, "vc-dir", "source")
524 self.workdir = os.path.join(self.slavebase, "vc-dir", "build")
525 # woo double-substitution
526 s = "s(%s, timeout=200, workdir='build', mode='%%s'" % (vctype,)
527 for k,v in args.items():
528 s += ", %s=%s" % (k, repr(v))
529 s += ")"
530 config = config_vc % s
531
532 m.loadConfig(config % 'clobber')
533 m.readConfig = True
534 m.startService()
535
536 d = self.connectSlave()
537 d.addCallback(lambda res: log.msg("testing clobber"))
538 d.addCallback(self._do_vctest_clobber)
539 d.addCallback(lambda res: log.msg("doing update"))
540 d.addCallback(lambda res: self.loadConfig(config % 'update'))
541 d.addCallback(lambda res: log.msg("testing update"))
542 d.addCallback(self._do_vctest_update)
543 if testRetry:
544 d.addCallback(lambda res: log.msg("testing update retry"))
545 d.addCallback(self._do_vctest_update_retry)
546 d.addCallback(lambda res: log.msg("doing copy"))
547 d.addCallback(lambda res: self.loadConfig(config % 'copy'))
548 d.addCallback(lambda res: log.msg("testing copy"))
549 d.addCallback(self._do_vctest_copy)
550 d.addCallback(lambda res: log.msg("did copy test"))
551 if self.metadir:
552 d.addCallback(lambda res: log.msg("doing export"))
553 d.addCallback(lambda res: self.loadConfig(config % 'export'))
554 d.addCallback(lambda res: log.msg("testing export"))
555 d.addCallback(self._do_vctest_export)
556 d.addCallback(lambda res: log.msg("did export test"))
557 return d
558
559 def _do_vctest_clobber(self, res):
560 d = self.doBuild() # initial checkout
561 d.addCallback(self._do_vctest_clobber_1)
562 return d
563 def _do_vctest_clobber_1(self, bs):
564 self.shouldExist(self.workdir, "main.c")
565 self.shouldExist(self.workdir, "version.c")
566 self.shouldExist(self.workdir, "subdir", "subdir.c")
567 if self.metadir:
568 self.shouldExist(self.workdir, self.metadir)
569 self.failUnlessEqual(bs.getProperty("revision"), None)
570 self.failUnlessEqual(bs.getProperty("branch"), None)
571 self.checkGotRevisionIsLatest(bs)
572
573 # Check that we can handle unfriendly permissions.
574 os.chmod(os.path.join(self.workdir, "subdir"), 0)
575 # Check that clobber really clobbers any old stuff.
576 self.touch(self.workdir, "newfile")
577 self.shouldExist(self.workdir, "newfile")
578 d = self.doBuild() # rebuild clobbers workdir
579 d.addCallback(self._do_vctest_clobber_2)
580 return d
581 def _do_vctest_clobber_2(self, res):
582 self.shouldNotExist(self.workdir, "newfile")
583 # do a checkout to a specific version. Mercurial-over-HTTP (when
584 # either client or server is older than hg-0.9.2) cannot do this
585 # directly, so it must checkout HEAD and then update back to the
586 # requested revision.
587 d = self.doBuild(ss=SourceStamp(revision=self.helper.trunk[0]))
588 d.addCallback(self._do_vctest_clobber_3)
589 return d
590 def _do_vctest_clobber_3(self, bs):
591 self.shouldExist(self.workdir, "main.c")
592 self.shouldExist(self.workdir, "version.c")
593 self.shouldExist(self.workdir, "subdir", "subdir.c")
594 if self.metadir:
595 self.shouldExist(self.workdir, self.metadir)
596 self.failUnlessEqual(bs.getProperty("revision"), self.helper.trunk[0] or None)
597 self.failUnlessEqual(bs.getProperty("branch"), None)
598 self.checkGotRevision(bs, self.helper.trunk[0])
599 # leave the tree at HEAD
600 return self.doBuild()
601
602
603 def _do_vctest_update(self, res):
604 log.msg("_do_vctest_update")
605 d = self.doBuild() # rebuild with update
606 d.addCallback(self._do_vctest_update_1)
607 return d
608 def _do_vctest_update_1(self, bs):
609 log.msg("_do_vctest_update_1")
610 self.shouldExist(self.workdir, "main.c")
611 self.shouldExist(self.workdir, "version.c")
612 self.shouldContain(self.workdir, "version.c",
613 "version=%d" % self.helper.version)
614 if self.metadir:
615 self.shouldExist(self.workdir, self.metadir)
616 self.failUnlessEqual(bs.getProperty("revision"), None)
617 self.checkGotRevisionIsLatest(bs)
618
619 self.touch(self.workdir, "newfile")
620 d = self.doBuild() # update rebuild leaves new files
621 d.addCallback(self._do_vctest_update_2)
622 return d
623 def _do_vctest_update_2(self, bs):
624 log.msg("_do_vctest_update_2")
625 self.shouldExist(self.workdir, "main.c")
626 self.shouldExist(self.workdir, "version.c")
627 self.touch(self.workdir, "newfile")
628 # now make a change to the repository and make sure we pick it up
629 d = self.helper.vc_revise()
630 d.addCallback(lambda res: self.doBuild())
631 d.addCallback(self._do_vctest_update_3)
632 return d
633 def _do_vctest_update_3(self, bs):
634 log.msg("_do_vctest_update_3")
635 self.shouldExist(self.workdir, "main.c")
636 self.shouldExist(self.workdir, "version.c")
637 self.shouldContain(self.workdir, "version.c",
638 "version=%d" % self.helper.version)
639 self.shouldExist(self.workdir, "newfile")
640 self.failUnlessEqual(bs.getProperty("revision"), None)
641 self.checkGotRevisionIsLatest(bs)
642
643 # now "update" to an older revision
644 d = self.doBuild(ss=SourceStamp(revision=self.helper.trunk[-2]))
645 d.addCallback(self._do_vctest_update_4)
646 return d
647 def _do_vctest_update_4(self, bs):
648 log.msg("_do_vctest_update_4")
649 self.shouldExist(self.workdir, "main.c")
650 self.shouldExist(self.workdir, "version.c")
651 self.shouldContain(self.workdir, "version.c",
652 "version=%d" % (self.helper.version-1))
653 self.failUnlessEqual(bs.getProperty("revision"),
654 self.helper.trunk[-2] or None)
655 self.checkGotRevision(bs, self.helper.trunk[-2])
656
657 # now update to the newer revision
658 d = self.doBuild(ss=SourceStamp(revision=self.helper.trunk[-1]))
659 d.addCallback(self._do_vctest_update_5)
660 return d
661 def _do_vctest_update_5(self, bs):
662 log.msg("_do_vctest_update_5")
663 self.shouldExist(self.workdir, "main.c")
664 self.shouldExist(self.workdir, "version.c")
665 self.shouldContain(self.workdir, "version.c",
666 "version=%d" % self.helper.version)
667 self.failUnlessEqual(bs.getProperty("revision"),
668 self.helper.trunk[-1] or None)
669 self.checkGotRevision(bs, self.helper.trunk[-1])
670
671
672 def _do_vctest_update_retry(self, res):
673 # certain local changes will prevent an update from working. The
674 # most common is to replace a file with a directory, or vice
675 # versa. The slave code should spot the failure and do a
676 # clobber/retry.
677 os.unlink(os.path.join(self.workdir, "main.c"))
678 os.mkdir(os.path.join(self.workdir, "main.c"))
679 self.touch(os.path.join(self.workdir, "main.c"), "foo")
680 self.touch(self.workdir, "newfile")
681
682 d = self.doBuild() # update, but must clobber to handle the error
683 d.addCallback(self._do_vctest_update_retry_1)
684 return d
685 def _do_vctest_update_retry_1(self, bs):
686 # SVN-1.4.0 doesn't seem to have any problem with the
687 # file-turned-directory issue (although older versions did). So don't
688 # actually check that the tree was clobbered.. as long as the update
689 # succeeded (checked by doBuild), that should be good enough.
690 #self.shouldNotExist(self.workdir, "newfile")
691 pass
692
693 def _do_vctest_copy(self, res):
694 log.msg("_do_vctest_copy 1")
695 d = self.doBuild() # copy rebuild clobbers new files
696 d.addCallback(self._do_vctest_copy_1)
697 return d
698 def _do_vctest_copy_1(self, bs):
699 log.msg("_do_vctest_copy 2")
700 if self.metadir:
701 self.shouldExist(self.workdir, self.metadir)
702 self.shouldNotExist(self.workdir, "newfile")
703 self.touch(self.workdir, "newfile")
704 self.touch(self.vcdir, "newvcfile")
705 self.failUnlessEqual(bs.getProperty("revision"), None)
706 self.checkGotRevisionIsLatest(bs)
707
708 d = self.doBuild() # copy rebuild clobbers new files
709 d.addCallback(self._do_vctest_copy_2)
710 return d
711 def _do_vctest_copy_2(self, bs):
712 log.msg("_do_vctest_copy 3")
713 if self.metadir:
714 self.shouldExist(self.workdir, self.metadir)
715 self.shouldNotExist(self.workdir, "newfile")
716 self.shouldExist(self.vcdir, "newvcfile")
717 self.shouldExist(self.workdir, "newvcfile")
718 self.failUnlessEqual(bs.getProperty("revision"), None)
719 self.checkGotRevisionIsLatest(bs)
720 self.touch(self.workdir, "newfile")
721
722 def _do_vctest_export(self, res):
723 d = self.doBuild() # export rebuild clobbers new files
724 d.addCallback(self._do_vctest_export_1)
725 return d
726 def _do_vctest_export_1(self, bs):
727 self.shouldNotExist(self.workdir, self.metadir)
728 self.shouldNotExist(self.workdir, "newfile")
729 self.failUnlessEqual(bs.getProperty("revision"), None)
730 #self.checkGotRevisionIsLatest(bs)
731 # VC 'export' is not required to have a got_revision
732 self.touch(self.workdir, "newfile")
733
734 d = self.doBuild() # export rebuild clobbers new files
735 d.addCallback(self._do_vctest_export_2)
736 return d
737 def _do_vctest_export_2(self, bs):
738 self.shouldNotExist(self.workdir, self.metadir)
739 self.shouldNotExist(self.workdir, "newfile")
740 self.failUnlessEqual(bs.getProperty("revision"), None)
741 #self.checkGotRevisionIsLatest(bs)
742 # VC 'export' is not required to have a got_revision
743
744 def do_patch(self, type):
745 vctype = self.vctype
746 args = self.helper.vcargs
747 m = self.master
748 self.vcdir = os.path.join(self.slavebase, "vc-dir", "source")
749 self.workdir = os.path.join(self.slavebase, "vc-dir", "build")
750 s = "s(%s, timeout=200, workdir='build', mode='%%s'" % (vctype,)
751 for k,v in args.items():
752 s += ", %s=%s" % (k, repr(v))
753 s += ")"
754 self.config = config_vc % s
755 if type == PATCHLEVEL0:
756 self.patch = (0, p0_diff)
757 elif type == SUBDIR_ROOT:
758 self.patch = (0, subdir_diff, 'subdir')
759 elif type == PATCHLEVEL2:
760 self.patch = (2, p2_diff)
761 else:
762 raise NotImplementedError
763 m.loadConfig(self.config % "clobber")
764 m.readConfig = True
765 m.startService()
766
767 ss = SourceStamp(revision=self.helper.trunk[-1], patch=self.patch)
768
769 d = self.connectSlave()
770 d.addCallback(lambda res: self.doBuild(ss=ss))
771 d.addCallback(self._doPatch_1)
772 return d
773 def _doPatch_1(self, bs):
774 self.shouldContain(self.workdir, "version.c",
775 "version=%d" % self.helper.version)
776 # make sure the file actually got patched
777 subdir_c = os.path.join(self.slavebase, "vc-dir", "build",
778 "subdir", "subdir.c")
779 data = open(subdir_c, "r").read()
780 self.failUnlessIn("HellÒ patched subdir.\\n", data)
781 self.failUnlessEqual(bs.getProperty("revision"),
782 self.helper.trunk[-1] or None)
783 self.checkGotRevision(bs, self.helper.trunk[-1])
784
785 # make sure that a rebuild does not use the leftover patched workdir
786 d = self.master.loadConfig(self.config % "update")
787 d.addCallback(lambda res: self.doBuild(ss=None))
788 d.addCallback(self._doPatch_2)
789 return d
790 def _doPatch_2(self, bs):
791 # make sure the file is back to its original
792 subdir_c = os.path.join(self.slavebase, "vc-dir", "build",
793 "subdir", "subdir.c")
794 data = open(subdir_c, "r").read()
795 self.failUnlessIn("Hello subdir.\\n", data)
796 self.failUnlessEqual(bs.getProperty("revision"), None)
797 self.checkGotRevisionIsLatest(bs)
798
799 # now make sure we can patch an older revision. We need at least two
800 # revisions here, so we might have to create one first
801 if len(self.helper.trunk) < 2:
802 d = self.helper.vc_revise()
803 d.addCallback(self._doPatch_3)
804 return d
805 return self._doPatch_3()
806
807 def _doPatch_3(self, res=None):
808 ss = SourceStamp(revision=self.helper.trunk[-2], patch=self.patch)
809 d = self.doBuild(ss=ss)
810 d.addCallback(self._doPatch_4)
811 return d
812 def _doPatch_4(self, bs):
813 self.shouldContain(self.workdir, "version.c",
814 "version=%d" % (self.helper.version-1))
815 # and make sure the file actually got patched
816 subdir_c = os.path.join(self.slavebase, "vc-dir", "build",
817 "subdir", "subdir.c")
818 data = open(subdir_c, "r").read()
819 self.failUnlessIn("HellÒ patched subdir.\\n", data)
820 self.failUnlessEqual(bs.getProperty("revision"),
821 self.helper.trunk[-2] or None)
822 self.checkGotRevision(bs, self.helper.trunk[-2])
823
824 # now check that we can patch a branch
825 ss = SourceStamp(branch=self.helper.branchname,
826 revision=self.helper.branch[-1],
827 patch=self.patch)
828 d = self.doBuild(ss=ss)
829 d.addCallback(self._doPatch_5)
830 return d
831 def _doPatch_5(self, bs):
832 self.shouldContain(self.workdir, "version.c",
833 "version=%d" % 1)
834 self.shouldContain(self.workdir, "main.c", "Hello branch.")
835 subdir_c = os.path.join(self.slavebase, "vc-dir", "build",
836 "subdir", "subdir.c")
837 data = open(subdir_c, "r").read()
838 self.failUnlessIn("HellÒ patched subdir.\\n", data)
839 self.failUnlessEqual(bs.getProperty("revision"),
840 self.helper.branch[-1] or None)
841 self.failUnlessEqual(bs.getProperty("branch"), self.helper.branchname or None)
842 self.checkGotRevision(bs, self.helper.branch[-1])
843
844
845 def do_vctest_once(self, shouldSucceed):
846 m = self.master
847 vctype = self.vctype
848 args = self.helper.vcargs
849 vcdir = os.path.join(self.slavebase, "vc-dir", "source")
850 workdir = os.path.join(self.slavebase, "vc-dir", "build")
851 # woo double-substitution
852 s = "s(%s, timeout=200, workdir='build', mode='clobber'" % (vctype,)
853 for k,v in args.items():
854 s += ", %s=%s" % (k, repr(v))
855 s += ")"
856 config = config_vc % s
857
858 m.loadConfig(config)
859 m.readConfig = True
860 m.startService()
861
862 self.connectSlave()
863 d = self.doBuild(shouldSucceed) # initial checkout
864 return d
865
866 def do_branch(self):
867 log.msg("do_branch")
868 vctype = self.vctype
869 args = self.helper.vcargs
870 m = self.master
871 self.vcdir = os.path.join(self.slavebase, "vc-dir", "source")
872 self.workdir = os.path.join(self.slavebase, "vc-dir", "build")
873 s = "s(%s, timeout=200, workdir='build', mode='%%s'" % (vctype,)
874 for k,v in args.items():
875 s += ", %s=%s" % (k, repr(v))
876 s += ")"
877 self.config = config_vc % s
878
879 m.loadConfig(self.config % "update")
880 m.readConfig = True
881 m.startService()
882
883 # first we do a build of the trunk
884 d = self.connectSlave()
885 d.addCallback(lambda res: self.doBuild(ss=SourceStamp()))
886 d.addCallback(self._doBranch_1)
887 return d
888 def _doBranch_1(self, bs):
889 log.msg("_doBranch_1")
890 # make sure the checkout was of the trunk
891 main_c = os.path.join(self.slavebase, "vc-dir", "build", "main.c")
892 data = open(main_c, "r").read()
893 self.failUnlessIn("Hello world.", data)
894
895 # now do a checkout on the branch. The change in branch name should
896 # trigger a clobber.
897 self.touch(self.workdir, "newfile")
898 d = self.doBuild(ss=SourceStamp(branch=self.helper.branchname))
899 d.addCallback(self._doBranch_2)
900 return d
901 def _doBranch_2(self, bs):
902 log.msg("_doBranch_2")
903 # make sure it was on the branch
904 main_c = os.path.join(self.slavebase, "vc-dir", "build", "main.c")
905 data = open(main_c, "r").read()
906 self.failUnlessIn("Hello branch.", data)
907 # and make sure the tree was clobbered
908 self.shouldNotExist(self.workdir, "newfile")
909
910 # doing another build on the same branch should not clobber the tree
911 self.touch(self.workdir, "newbranchfile")
912 d = self.doBuild(ss=SourceStamp(branch=self.helper.branchname))
913 d.addCallback(self._doBranch_3)
914 return d
915 def _doBranch_3(self, bs):
916 log.msg("_doBranch_3")
917 # make sure it is still on the branch
918 main_c = os.path.join(self.slavebase, "vc-dir", "build", "main.c")
919 data = open(main_c, "r").read()
920 self.failUnlessIn("Hello branch.", data)
921 # and make sure the tree was not clobbered
922 self.shouldExist(self.workdir, "newbranchfile")
923
924 # now make sure that a non-branch checkout clobbers the tree
925 d = self.doBuild(ss=SourceStamp())
926 d.addCallback(self._doBranch_4)
927 return d
928 def _doBranch_4(self, bs):
929 log.msg("_doBranch_4")
930 # make sure it was on the trunk
931 main_c = os.path.join(self.slavebase, "vc-dir", "build", "main.c")
932 data = open(main_c, "r").read()
933 self.failUnlessIn("Hello world.", data)
934 self.shouldNotExist(self.workdir, "newbranchfile")
935
936 def do_getpatch(self, doBranch=True):
937 log.msg("do_getpatch")
938 # prepare a buildslave to do checkouts
939 vctype = self.vctype
940 args = self.helper.vcargs
941 m = self.master
942 self.vcdir = os.path.join(self.slavebase, "vc-dir", "source")
943 self.workdir = os.path.join(self.slavebase, "vc-dir", "build")
944 # woo double-substitution
945 s = "s(%s, timeout=200, workdir='build', mode='%%s'" % (vctype,)
946 for k,v in args.items():
947 s += ", %s=%s" % (k, repr(v))
948 s += ")"
949 config = config_vc % s
950
951 m.loadConfig(config % 'clobber')
952 m.readConfig = True
953 m.startService()
954
955 d = self.connectSlave()
956
957 # then set up the "developer's tree". first we modify a tree from the
958 # head of the trunk
959 tmpdir = "try_workdir"
960 self.trydir = os.path.join(self.helper.repbase, tmpdir)
961 rmdirRecursive(self.trydir)
962 d.addCallback(self.do_getpatch_trunkhead)
963 d.addCallback(self.do_getpatch_trunkold)
964 if doBranch:
965 d.addCallback(self.do_getpatch_branch)
966 d.addCallback(self.do_getpatch_finish)
967 return d
968
969 def do_getpatch_finish(self, res):
970 log.msg("do_getpatch_finish")
971 self.helper.vc_try_finish(self.trydir)
972 return res
973
974 def try_shouldMatch(self, filename):
975 devfilename = os.path.join(self.trydir, filename)
976 devfile = open(devfilename, "r").read()
977 slavefilename = os.path.join(self.workdir, filename)
978 slavefile = open(slavefilename, "r").read()
979 self.failUnlessEqual(devfile, slavefile,
980 ("slavefile (%s) contains '%s'. "
981 "developer's file (%s) contains '%s'. "
982 "These ought to match") %
983 (slavefilename, slavefile,
984 devfilename, devfile))
985
986 def do_getpatch_trunkhead(self, res):
987 log.msg("do_getpatch_trunkhead")
988 d = self.helper.vc_try_checkout(self.trydir, self.helper.trunk[-1])
989 d.addCallback(self._do_getpatch_trunkhead_1)
990 return d
991 def _do_getpatch_trunkhead_1(self, res):
992 log.msg("_do_getpatch_trunkhead_1")
993 d = tryclient.getSourceStamp(self.vctype_try, self.trydir, None)
994 d.addCallback(self._do_getpatch_trunkhead_2)
995 return d
996 def _do_getpatch_trunkhead_2(self, ss):
997 log.msg("_do_getpatch_trunkhead_2")
998 d = self.doBuild(ss=ss)
999 d.addCallback(self._do_getpatch_trunkhead_3)
1000 return d
1001 def _do_getpatch_trunkhead_3(self, res):
1002 log.msg("_do_getpatch_trunkhead_3")
1003 # verify that the resulting buildslave tree matches the developer's
1004 self.try_shouldMatch("main.c")
1005 self.try_shouldMatch("version.c")
1006 self.try_shouldMatch(os.path.join("subdir", "subdir.c"))
1007
1008 def do_getpatch_trunkold(self, res):
1009 log.msg("do_getpatch_trunkold")
1010 # now try a tree from an older revision. We need at least two
1011 # revisions here, so we might have to create one first
1012 if len(self.helper.trunk) < 2:
1013 d = self.helper.vc_revise()
1014 d.addCallback(self._do_getpatch_trunkold_1)
1015 return d
1016 return self._do_getpatch_trunkold_1()
1017 def _do_getpatch_trunkold_1(self, res=None):
1018 log.msg("_do_getpatch_trunkold_1")
1019 d = self.helper.vc_try_checkout(self.trydir, self.helper.trunk[-2])
1020 d.addCallback(self._do_getpatch_trunkold_2)
1021 return d
1022 def _do_getpatch_trunkold_2(self, res):
1023 log.msg("_do_getpatch_trunkold_2")
1024 d = tryclient.getSourceStamp(self.vctype_try, self.trydir, None)
1025 d.addCallback(self._do_getpatch_trunkold_3)
1026 return d
1027 def _do_getpatch_trunkold_3(self, ss):
1028 log.msg("_do_getpatch_trunkold_3")
1029 d = self.doBuild(ss=ss)
1030 d.addCallback(self._do_getpatch_trunkold_4)
1031 return d
1032 def _do_getpatch_trunkold_4(self, res):
1033 log.msg("_do_getpatch_trunkold_4")
1034 # verify that the resulting buildslave tree matches the developer's
1035 self.try_shouldMatch("main.c")
1036 self.try_shouldMatch("version.c")
1037 self.try_shouldMatch(os.path.join("subdir", "subdir.c"))
1038
1039 def do_getpatch_branch(self, res):
1040 log.msg("do_getpatch_branch")
1041 # now try a tree from a branch
1042 d = self.helper.vc_try_checkout(self.trydir, self.helper.branch[-1],
1043 self.helper.branchname)
1044 d.addCallback(self._do_getpatch_branch_1)
1045 return d
1046 def _do_getpatch_branch_1(self, res):
1047 log.msg("_do_getpatch_branch_1")
1048 d = tryclient.getSourceStamp(self.vctype_try, self.trydir,
1049 self.helper.try_branchname)
1050 d.addCallback(self._do_getpatch_branch_2)
1051 return d
1052 def _do_getpatch_branch_2(self, ss):
1053 log.msg("_do_getpatch_branch_2")
1054 d = self.doBuild(ss=ss)
1055 d.addCallback(self._do_getpatch_branch_3)
1056 return d
1057 def _do_getpatch_branch_3(self, res):
1058 log.msg("_do_getpatch_branch_3")
1059 # verify that the resulting buildslave tree matches the developer's
1060 self.try_shouldMatch("main.c")
1061 self.try_shouldMatch("version.c")
1062 self.try_shouldMatch(os.path.join("subdir", "subdir.c"))
1063
1064
1065 def dumpPatch(self, patch):
1066 # FIXME: This function is never called.
1067 raise NotImplementedError
1068 # this exists to help me figure out the right 'patchlevel' value
1069 # should be returned by tryclient.getSourceStamp
1070 n = self.mktemp()
1071 open(n,"w").write(patch)
1072 d = self.runCommand(".", ["lsdiff", n])
1073 def p(res): print "lsdiff:", res.strip().split("\n")
1074 d.addCallback(p)
1075 return d
1076
1077
1078 def tearDown(self):
1079 self.tearDownSignalHandler()
1080 d = defer.succeed(None)
1081 if self.slave:
1082 d2 = self.master.botmaster.waitUntilBuilderDetached("vç")
1083 d.addCallback(lambda res: self.slave.stopService())
1084 d.addCallback(lambda res: d2)
1085 if self.master:
1086 d.addCallback(lambda res: self.master.stopService())
1087 if self.httpServer:
1088 d.addCallback(lambda res: self.httpServer.stopListening())
1089 def stopHTTPTimer():
1090 from twisted.web import http
1091 http._logDateTimeStop() # shut down the internal timer. DUMB!
1092 d.addCallback(lambda res: stopHTTPTimer())
1093 d.addCallback(lambda res: self.tearDown2())
1094 return d
1095
1096 def tearDown2(self):
1097 pass
1098
1099 class CVSHelper(BaseHelper):
1100 branchname = "branch"
1101 try_branchname = "branch"
1102
1103 def capable(self):
1104 cvspaths = which('cvs')
1105 if not cvspaths:
1106 return (False, "CVS is not installed")
1107 # cvs-1.10 (as shipped with OS-X 10.3 "Panther") is too old for this
1108 # test. There is a situation where we check out a tree, make a
1109 # change, then commit it back, and CVS refuses to believe that we're
1110 # operating in a CVS tree. I tested cvs-1.12.9 and it works ok, OS-X
1111 # 10.4 "Tiger" comes with cvs-1.11, but I haven't tested that yet.
1112 # For now, skip the tests if we've got 1.10 .
1113 log.msg("running %s --version.." % (cvspaths[0],))
1114 d = utils.getProcessOutput(cvspaths[0], ["--version"],
1115 env=os.environ)
1116 d.addCallback(self._capable, cvspaths[0])
1117 return d
1118
1119 def _capable(self, v, vcexe):
1120 # Consider also CVS for NT which announces itself like
1121 # Concurrent Versions System (CVSNT) 2.0.51d (client/server)
1122 m = re.search(r'\(CVS[NT]*\) ([\d.]+)', v)
1123 if not m:
1124 log.msg("couldn't identify CVS version number in output:")
1125 log.msg("'''%s'''" % v)
1126 log.msg("skipping tests")
1127 return (False, "Found CVS but couldn't identify its version")
1128 ver = m.group(1)
1129 log.msg("found CVS version '%s'" % ver)
1130 if ver == "1.10":
1131 return (False, "Found CVS, but it is too old")
1132 self.vcexe = vcexe
1133 return (True, None)
1134
1135 def getdate(self):
1136 # this timestamp is eventually passed to CVS in a -D argument, and
1137 # strftime's %z specifier doesn't seem to work reliably (I get +0000
1138 # where I should get +0700 under linux sometimes, and windows seems
1139 # to want to put a verbose 'Eastern Standard Time' in there), so
1140 # leave off the timezone specifier and treat this as localtime. A
1141 # valid alternative would be to use a hard-coded +0000 and
1142 # time.gmtime().
1143 return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
1144
1145 def createRepository(self):
1146 self.createBasedir()
1147 self.cvsrep = cvsrep = os.path.join(self.repbase, "CVS-Repository")
1148 tmp = os.path.join(self.repbase, "cvstmp")
1149
1150 w = self.dovc(self.repbase, ['-d', cvsrep, 'init'])
1151 yield w; w.getResult() # we must getResult() to raise any exceptions
1152
1153 self.populate(tmp)
1154 cmd = ['-d', self.cvsrep, 'import',
1155 '-m', 'sample_project_files', 'sample', 'vendortag', 'start']
1156 w = self.dovc(tmp, cmd)
1157 yield w; w.getResult()
1158 rmdirRecursive(tmp)
1159 # take a timestamp as the first revision number
1160 time.sleep(2)
1161 self.addTrunkRev(self.getdate())
1162 time.sleep(2)
1163
1164 w = self.dovc(self.repbase,
1165 ['-d', self.cvsrep, 'checkout', '-d', 'cvstmp', 'sample '])
1166 yield w; w.getResult()
1167
1168 w = self.dovc(tmp, ['tag', '-b', self.branchname])
1169 yield w; w.getResult()
1170 self.populate_branch(tmp)
1171 w = self.dovc(tmp,
1172 ['commit', '-m', 'commit_on_branch', '-r', self.branchn ame])
1173 yield w; w.getResult()
1174 rmdirRecursive(tmp)
1175 time.sleep(2)
1176 self.addBranchRev(self.getdate())
1177 time.sleep(2)
1178 self.vcargs = { 'cvsroot': self.cvsrep, 'cvsmodule': "sample" }
1179 createRepository = deferredGenerator(createRepository)
1180
1181
1182 def vc_revise(self):
1183 tmp = os.path.join(self.repbase, "cvstmp")
1184
1185 w = self.dovc(self.repbase,
1186 ['-d', self.cvsrep, 'checkout', '-d', 'cvstmp', 'sampl e'])
1187 yield w; w.getResult()
1188 self.version += 1
1189 version_c = VERSION_C % self.version
1190 open(os.path.join(tmp, "version.c"), "w").write(version_c)
1191 w = self.dovc(tmp,
1192 ['commit', '-m', 'revised_to_%d' % self.version, 'versi on.c'])
1193 yield w; w.getResult()
1194 rmdirRecursive(tmp)
1195 time.sleep(2)
1196 self.addTrunkRev(self.getdate())
1197 time.sleep(2)
1198 vc_revise = deferredGenerator(vc_revise)
1199
1200 def vc_try_checkout(self, workdir, rev, branch=None):
1201 # 'workdir' is an absolute path
1202
1203 # get rid of timezone info, which might not be parsed
1204 rev = re.sub("[^0-9 :-]","",rev)
1205 rev = re.sub(" ","",rev)
1206 assert os.path.abspath(workdir) == workdir
1207 cmd = ["-d", self.cvsrep, "checkout",
1208 "-d", workdir,
1209 "-D", rev]
1210 if branch is not None:
1211 cmd.append("-r")
1212 cmd.append(branch)
1213 cmd.append("sample")
1214 w = self.dovc(self.repbase, cmd)
1215 yield w; w.getResult()
1216 open(os.path.join(workdir, "subdir", "subdir.c"), "w").write(TRY_C)
1217 vc_try_checkout = deferredGenerator(vc_try_checkout)
1218
1219 def vc_try_finish(self, workdir):
1220 rmdirRecursive(workdir)
1221
1222 class CVS(VCBase, unittest.TestCase):
1223 vc_name = "cvs"
1224
1225 metadir = "CVS"
1226 vctype = "source.CVS"
1227 vctype_try = "cvs"
1228 # CVS gives us got_revision, but it is based entirely upon the local
1229 # clock, which means it is unlikely to match the timestamp taken earlier.
1230 # This might be enough for common use, but won't be good enough for our
1231 # tests to accept, so pretend it doesn't have got_revision at all.
1232 has_got_revision = False
1233
1234 def testCheckout(self):
1235 d = self.do_vctest()
1236 return d
1237
1238 def testPatch(self):
1239 d = self.do_patch(PATCHLEVEL0)
1240 return d
1241
1242 def testPatchSubDir(self):
1243 d = self.do_patch(SUBDIR_ROOT)
1244 return d
1245
1246 def testPatchP2(self):
1247 d = self.do_patch(PATCHLEVEL2)
1248 return d
1249
1250 def testCheckoutBranch(self):
1251 d = self.do_branch()
1252 return d
1253
1254 def testTry(self):
1255 d = self.do_getpatch(doBranch=False)
1256 return d
1257
1258 VCS.registerVC(CVS.vc_name, CVSHelper())
1259
1260 class CVSHelper_checkout_options(CVSHelper):
1261 """
1262 Specialized CVSHelper to set checkout_options to verify that it works
1263 """
1264 def createRepository(self):
1265 self.createBasedir()
1266 self.cvsrep = cvsrep = os.path.join(self.repbase, "CVS-Repository")
1267 tmp = os.path.join(self.repbase, "cvstmp")
1268
1269 w = self.dovc(self.repbase, ['-d', cvsrep, 'init'])
1270 yield w; w.getResult() # we must getResult() to raise any exceptions
1271
1272 self.populate(tmp)
1273 cmd = ['-d', self.cvsrep, 'import',
1274 '-m', 'sample_project_files', 'sample', 'vendortag', 'start']
1275 w = self.dovc(tmp, cmd)
1276 yield w; w.getResult()
1277 rmdirRecursive(tmp)
1278 # take a timestamp as the first revision number
1279 time.sleep(2)
1280 self.addTrunkRev(self.getdate())
1281 time.sleep(2)
1282
1283 w = self.dovc(self.repbase,
1284 ['-d', self.cvsrep, 'checkout', '-d', 'cvstmp', 'sample '])
1285 yield w; w.getResult()
1286
1287 w = self.dovc(tmp, ['tag', '-b', self.branchname])
1288 yield w; w.getResult()
1289 self.populate_branch(tmp)
1290 w = self.dovc(tmp,
1291 ['commit', '-m', 'commit_on_branch', '-r', self.branchn ame])
1292 yield w; w.getResult()
1293 rmdirRecursive(tmp)
1294 time.sleep(2)
1295 self.addBranchRev(self.getdate())
1296 time.sleep(2)
1297 self.vcargs = { 'cvsroot': self.cvsrep,
1298 'cvsmodule': "sample",
1299 'checkout_options':["-P"] }
1300 createRepository = deferredGenerator(createRepository)
1301
1302
1303 class CVS_checkout_options(CVS):
1304 """
1305 Specialized CVS_checkout_options class to use with
1306 CVSHelper_checkout_options
1307 set checkout_options to verify that it works
1308 """
1309 vc_name = "cvs"
1310
1311 metadir = "CVS"
1312 vctype = "source.CVS"
1313 vctype_try = "cvs"
1314 # CVS gives us got_revision, but it is based entirely upon the local
1315 # clock, which means it is unlikely to match the timestamp taken earlier.
1316 # This might be enough for common use, but won't be good enough for our
1317 # tests to accept, so pretend it doesn't have got_revision at all.
1318 has_got_revision = False
1319
1320 VCS.registerVC(CVS_checkout_options.vc_name, CVSHelper_checkout_options())
1321
1322
1323 class SVNHelper(BaseHelper):
1324 branchname = "sample/branch"
1325 try_branchname = "sample/branch"
1326
1327 def capable(self):
1328 svnpaths = which('svn')
1329 svnadminpaths = which('svnadmin')
1330 if not svnpaths:
1331 return (False, "SVN is not installed")
1332 if not svnadminpaths:
1333 return (False, "svnadmin is not installed")
1334 # we need svn to be compiled with the ra_local access
1335 # module
1336 log.msg("running svn --version..")
1337 env = os.environ.copy()
1338 env['LC_ALL'] = "C"
1339 d = utils.getProcessOutput(svnpaths[0], ["--version"],
1340 env=env)
1341 d.addCallback(self._capable, svnpaths[0], svnadminpaths[0])
1342 return d
1343
1344 def _capable(self, v, vcexe, svnadmin):
1345 if v.find("handles 'file' schem") != -1:
1346 # older versions say 'schema', 1.2.0 and beyond say 'scheme'
1347 self.vcexe = vcexe
1348 self.svnadmin = svnadmin
1349 return (True, None)
1350 excuse = ("%s found but it does not support 'file:' " +
1351 "schema, skipping svn tests") % vcexe
1352 log.msg(excuse)
1353 return (False, excuse)
1354
1355 def createRepository(self):
1356 self.createBasedir()
1357 self.svnrep = os.path.join(self.repbase,
1358 "SVN-Repository").replace('\\','/')
1359 tmp = os.path.join(self.repbase, "svntmp")
1360 if sys.platform == 'win32':
1361 # On Windows Paths do not start with a /
1362 self.svnurl = "file:///%s" % self.svnrep
1363 else:
1364 self.svnurl = "file://%s" % self.svnrep
1365 self.svnurl_trunk = self.svnurl + "/sample/trunk"
1366 self.svnurl_branch = self.svnurl + "/sample/branch"
1367
1368 w = self.do(self.repbase, [self.svnadmin, "create", self.svnrep])
1369 yield w; w.getResult()
1370
1371 self.populate(tmp)
1372 w = self.dovc(tmp,
1373 ['import', '-m', 'sample_project_files', self.svnurl_tr unk])
1374 yield w; out = w.getResult()
1375 rmdirRecursive(tmp)
1376 m = re.search(r'Committed revision (\d+)\.', out)
1377 assert m.group(1) == "1" # first revision is always "1"
1378 self.addTrunkRev(int(m.group(1)))
1379
1380 w = self.dovc(self.repbase,
1381 ['checkout', self.svnurl_trunk, 'svntmp'])
1382 yield w; w.getResult()
1383
1384 w = self.dovc(tmp, ['cp', '-m' , 'make_branch', self.svnurl_trunk,
1385 self.svnurl_branch])
1386 yield w; w.getResult()
1387 w = self.dovc(tmp, ['switch', self.svnurl_branch])
1388 yield w; w.getResult()
1389 self.populate_branch(tmp)
1390 w = self.dovc(tmp, ['commit', '-m', 'commit_on_branch'])
1391 yield w; out = w.getResult()
1392 rmdirRecursive(tmp)
1393 m = re.search(r'Committed revision (\d+)\.', out)
1394 self.addBranchRev(int(m.group(1)))
1395 createRepository = deferredGenerator(createRepository)
1396
1397 def vc_revise(self):
1398 tmp = os.path.join(self.repbase, "svntmp")
1399 rmdirRecursive(tmp)
1400 log.msg("vc_revise" + self.svnurl_trunk)
1401 w = self.dovc(self.repbase,
1402 ['checkout', self.svnurl_trunk, 'svntmp'])
1403 yield w; w.getResult()
1404 self.version += 1
1405 version_c = VERSION_C % self.version
1406 open(os.path.join(tmp, "version.c"), "w").write(version_c)
1407 w = self.dovc(tmp, ['commit', '-m', 'revised_to_%d' % self.version])
1408 yield w; out = w.getResult()
1409 m = re.search(r'Committed revision (\d+)\.', out)
1410 self.addTrunkRev(int(m.group(1)))
1411 rmdirRecursive(tmp)
1412 vc_revise = deferredGenerator(vc_revise)
1413
1414 def vc_try_checkout(self, workdir, rev, branch=None):
1415 assert os.path.abspath(workdir) == workdir
1416 if os.path.exists(workdir):
1417 rmdirRecursive(workdir)
1418 if not branch:
1419 svnurl = self.svnurl_trunk
1420 else:
1421 # N.B.: this is *not* os.path.join: SVN URLs use slashes
1422 # regardless of the host operating system's filepath separator
1423 svnurl = self.svnurl + "/" + branch
1424 w = self.dovc(self.repbase,
1425 ['checkout', svnurl, workdir])
1426 yield w; w.getResult()
1427 open(os.path.join(workdir, "subdir", "subdir.c"), "w").write(TRY_C)
1428 vc_try_checkout = deferredGenerator(vc_try_checkout)
1429
1430 def vc_try_finish(self, workdir):
1431 rmdirRecursive(workdir)
1432
1433
1434 class SVN(VCBase, unittest.TestCase):
1435 vc_name = "svn"
1436
1437 metadir = ".svn"
1438 vctype = "source.SVN"
1439 vctype_try = "svn"
1440 has_got_revision = True
1441 has_got_revision_branches_are_merged = True
1442
1443 def testCheckout(self):
1444 # we verify this one with the svnurl style of vcargs. We test the
1445 # baseURL/defaultBranch style in testPatch and testCheckoutBranch.
1446 self.helper.vcargs = { 'svnurl': self.helper.svnurl_trunk }
1447 d = self.do_vctest()
1448 return d
1449
1450 def testPatch(self):
1451 self.helper.vcargs = { 'baseURL': self.helper.svnurl + "/",
1452 'defaultBranch': "sample/trunk",
1453 }
1454 d = self.do_patch(PATCHLEVEL0)
1455 return d
1456
1457 def testPatchSubDir(self):
1458 self.helper.vcargs = { 'baseURL': self.helper.svnurl + "/",
1459 'defaultBranch': "sample/trunk",
1460 }
1461 d = self.do_patch(SUBDIR_ROOT)
1462 return d
1463
1464 def testPatchP2(self):
1465 self.helper.vcargs = { 'baseURL': self.helper.svnurl + "/",
1466 'defaultBranch': "sample/trunk",
1467 }
1468 d = self.do_patch(PATCHLEVEL2)
1469 return d
1470
1471 def testCheckoutBranch(self):
1472 self.helper.vcargs = { 'baseURL': self.helper.svnurl + "/",
1473 'defaultBranch': "sample/trunk",
1474 }
1475 d = self.do_branch()
1476 return d
1477
1478 def testTry(self):
1479 # extract the base revision and patch from a modified tree, use it to
1480 # create the same contents on the buildslave
1481 self.helper.vcargs = { 'baseURL': self.helper.svnurl + "/",
1482 'defaultBranch': "sample/trunk",
1483 }
1484 d = self.do_getpatch()
1485 return d
1486
1487 ## can't test the username= and password= options, because we do not have an
1488 ## svn repository that requires authentication.
1489
1490 VCS.registerVC(SVN.vc_name, SVNHelper())
1491
1492
1493 class P4Helper(BaseHelper):
1494 branchname = "branch"
1495 p4port = 'localhost:1666'
1496 pid = None
1497 base_descr = 'Change: new\nDescription: asdf\nFiles:\n'
1498
1499 def capable(self):
1500 p4paths = which('p4')
1501 p4dpaths = which('p4d')
1502 if not p4paths:
1503 return (False, "p4 is not installed")
1504 if not p4dpaths:
1505 return (False, "p4d is not installed")
1506 self.vcexe = p4paths[0]
1507 self.p4dexe = p4dpaths[0]
1508 return (True, None)
1509
1510 class _P4DProtocol(protocol.ProcessProtocol):
1511 def __init__(self):
1512 self.started = defer.Deferred()
1513 self.ended = defer.Deferred()
1514
1515 def outReceived(self, data):
1516 # When it says starting, it has bound to the socket.
1517 if self.started:
1518 #
1519 # Make sure p4d has started. Newer versions of p4d
1520 # have more verbose messaging when db files don't exist, so
1521 # we use re.search instead of startswith.
1522 #
1523 if re.search('Perforce Server starting...', data):
1524 self.started.callback(None)
1525 else:
1526 print "p4d said %r" % data
1527 try:
1528 raise Exception('p4d said %r' % data)
1529 except:
1530 self.started.errback(failure.Failure())
1531 self.started = None
1532
1533 def errReceived(self, data):
1534 print "p4d stderr: %s" % data
1535
1536 def processEnded(self, status_object):
1537 if status_object.check(error.ProcessDone):
1538 self.ended.callback(None)
1539 else:
1540 self.ended.errback(status_object)
1541
1542 def _start_p4d(self):
1543 proto = self._P4DProtocol()
1544 reactor.spawnProcess(proto, self.p4dexe, ['p4d', '-p', self.p4port],
1545 env=os.environ, path=self.p4rep)
1546 return proto.started, proto.ended
1547
1548 def dop4(self, basedir, command, failureIsOk=False, stdin=None):
1549 # p4 looks at $PWD instead of getcwd(), which causes confusion when
1550 # we spawn commands without an intervening shell (sh -c). We can
1551 # override this with a -d argument.
1552 command = "-p %s -d %s %s" % (self.p4port, basedir, command)
1553 return self.dovc(basedir, command, failureIsOk, stdin)
1554
1555 def createRepository(self):
1556 # this is only called once per VC system, so start p4d here.
1557
1558 self.createBasedir()
1559 tmp = os.path.join(self.repbase, "p4tmp")
1560 self.p4rep = os.path.join(self.repbase, 'P4-Repository')
1561 os.mkdir(self.p4rep)
1562
1563 # Launch p4d.
1564 started, self.p4d_shutdown = self._start_p4d()
1565 w = waitForDeferred(started)
1566 yield w; w.getResult()
1567
1568 # Create client spec.
1569 os.mkdir(tmp)
1570 clispec = 'Client: creator\n'
1571 clispec += 'Root: %s\n' % tmp
1572 clispec += 'View:\n'
1573 clispec += '\t//depot/... //creator/...\n'
1574 w = self.dop4(tmp, 'client -i', stdin=clispec)
1575 yield w; w.getResult()
1576
1577 # Create first rev (trunk).
1578 self.populate(os.path.join(tmp, 'trunk'))
1579 files = ['main.c', 'version.c', 'subdir/subdir.c']
1580 w = self.dop4(tmp, "-c creator add "
1581 + " ".join(['trunk/%s' % f for f in files]))
1582 yield w; w.getResult()
1583 descr = self.base_descr
1584 for file in files:
1585 descr += '\t//depot/trunk/%s\n' % file
1586 w = self.dop4(tmp, "-c creator submit -i", stdin=descr)
1587 yield w; out = w.getResult()
1588 m = re.search(r'Change (\d+) submitted.', out)
1589 assert m.group(1) == '1'
1590 self.addTrunkRev(m.group(1))
1591
1592 # Create second rev (branch).
1593 w = self.dop4(tmp, '-c creator integrate '
1594 + '//depot/trunk/... //depot/branch/...')
1595 yield w; w.getResult()
1596 w = self.dop4(tmp, "-c creator edit branch/main.c")
1597 yield w; w.getResult()
1598 self.populate_branch(os.path.join(tmp, 'branch'))
1599 descr = self.base_descr
1600 for file in files:
1601 descr += '\t//depot/branch/%s\n' % file
1602 w = self.dop4(tmp, "-c creator submit -i", stdin=descr)
1603 yield w; out = w.getResult()
1604 m = re.search(r'Change (\d+) submitted.', out)
1605 self.addBranchRev(m.group(1))
1606 createRepository = deferredGenerator(createRepository)
1607
1608 def vc_revise(self):
1609 tmp = os.path.join(self.repbase, "p4tmp")
1610 self.version += 1
1611 version_c = VERSION_C % self.version
1612 w = self.dop4(tmp, '-c creator edit trunk/version.c')
1613 yield w; w.getResult()
1614 open(os.path.join(tmp, "trunk/version.c"), "w").write(version_c)
1615 descr = self.base_descr + '\t//depot/trunk/version.c\n'
1616 w = self.dop4(tmp, "-c creator submit -i", stdin=descr)
1617 yield w; out = w.getResult()
1618 m = re.search(r'Change (\d+) submitted.', out)
1619 self.addTrunkRev(m.group(1))
1620 vc_revise = deferredGenerator(vc_revise)
1621
1622 def shutdown_p4d(self):
1623 d = self.runCommand(self.repbase, '%s -p %s admin stop'
1624 % (self.vcexe, self.p4port))
1625 return d.addCallback(lambda _: self.p4d_shutdown)
1626
1627 class P4(VCBase, unittest.TestCase):
1628 metadir = None
1629 vctype = "source.P4"
1630 vc_name = "p4"
1631 has_got_revision = True
1632
1633 def tearDownClass(self):
1634 if self.helper:
1635 return self.helper.shutdown_p4d()
1636
1637 def testCheckout(self):
1638 self.helper.vcargs = { 'p4port': self.helper.p4port,
1639 'p4base': '//depot/',
1640 'defaultBranch': 'trunk' }
1641 d = self.do_vctest(testRetry=False)
1642 # TODO: like arch and darcs, sync does nothing when server is not
1643 # changed.
1644 return d
1645
1646 def testCheckoutBranch(self):
1647 self.helper.vcargs = { 'p4port': self.helper.p4port,
1648 'p4base': '//depot/',
1649 'defaultBranch': 'trunk' }
1650 d = self.do_branch()
1651 return d
1652
1653 def testPatch(self):
1654 self.helper.vcargs = { 'p4port': self.helper.p4port,
1655 'p4base': '//depot/',
1656 'defaultBranch': 'trunk' }
1657 d = self.do_patch(PATCHLEVEL0)
1658 return d
1659
1660 def testPatchSubDir(self):
1661 self.helper.vcargs = { 'p4port': self.helper.p4port,
1662 'p4base': '//depot/',
1663 'defaultBranch': 'trunk' }
1664 d = self.do_patch(SUBDIR_ROOT)
1665 return d
1666
1667 def testPatchP2(self):
1668 self.helper.vcargs = { 'p4port': self.helper.p4port,
1669 'p4base': '//depot/',
1670 'defaultBranch': 'trunk' }
1671 d = self.do_patch(PATCHLEVEL2)
1672 return d
1673
1674 VCS.registerVC(P4.vc_name, P4Helper())
1675
1676
1677 class DarcsHelper(BaseHelper):
1678 branchname = "branch"
1679 try_branchname = "branch"
1680
1681 def capable(self):
1682 darcspaths = which('darcs')
1683 if not darcspaths:
1684 return (False, "Darcs is not installed")
1685 self.vcexe = darcspaths[0]
1686 return (True, None)
1687
1688 def createRepository(self):
1689 self.createBasedir()
1690 self.darcs_base = os.path.join(self.repbase, "Darcs-Repository")
1691 self.rep_trunk = os.path.join(self.darcs_base, "trunk")
1692 self.rep_branch = os.path.join(self.darcs_base, "branch")
1693 tmp = os.path.join(self.repbase, "darcstmp")
1694
1695 _makedirsif(self.rep_trunk)
1696 w = self.dovc(self.rep_trunk, ["initialize"])
1697 yield w; w.getResult()
1698 _makedirsif(self.rep_branch)
1699 w = self.dovc(self.rep_branch, ["initialize"])
1700 yield w; w.getResult()
1701
1702 self.populate(tmp)
1703 w = self.dovc(tmp, qw("initialize"))
1704 yield w; w.getResult()
1705 w = self.dovc(tmp, qw("add -r ."))
1706 yield w; w.getResult()
1707 w = self.dovc(tmp, qw("record -a -m initial_import --skip-long-comment - A test@buildbot.sf.net"))
1708 yield w; w.getResult()
1709 w = self.dovc(tmp, ["push", "-a", self.rep_trunk])
1710 yield w; w.getResult()
1711 w = self.dovc(tmp, qw("changes --context"))
1712 yield w; out = w.getResult()
1713 self.addTrunkRev(out)
1714
1715 self.populate_branch(tmp)
1716 w = self.dovc(tmp, qw("record -a --ignore-times -m commit_on_branch --sk ip-long-comment -A test@buildbot.sf.net"))
1717 yield w; w.getResult()
1718 w = self.dovc(tmp, ["push", "-a", self.rep_branch])
1719 yield w; w.getResult()
1720 w = self.dovc(tmp, qw("changes --context"))
1721 yield w; out = w.getResult()
1722 self.addBranchRev(out)
1723 rmdirRecursive(tmp)
1724 createRepository = deferredGenerator(createRepository)
1725
1726 def vc_revise(self):
1727 tmp = os.path.join(self.repbase, "darcstmp")
1728 _makedirsif(tmp)
1729 w = self.dovc(tmp, qw("initialize"))
1730 yield w; w.getResult()
1731 w = self.dovc(tmp, ["pull", "-a", self.rep_trunk])
1732 yield w; w.getResult()
1733
1734 self.version += 1
1735 version_c = VERSION_C % self.version
1736 open(os.path.join(tmp, "version.c"), "w").write(version_c)
1737 w = self.dovc(tmp, qw("record -a --ignore-times -m revised_to_%d --skip- long-comment -A test@buildbot.sf.net" % self.version))
1738 yield w; w.getResult()
1739 w = self.dovc(tmp, ["push", "-a", self.rep_trunk])
1740 yield w; w.getResult()
1741 w = self.dovc(tmp, qw("changes --context"))
1742 yield w; out = w.getResult()
1743 self.addTrunkRev(out)
1744 rmdirRecursive(tmp)
1745 vc_revise = deferredGenerator(vc_revise)
1746
1747 def vc_try_checkout(self, workdir, rev, branch=None):
1748 assert os.path.abspath(workdir) == workdir
1749 if os.path.exists(workdir):
1750 rmdirRecursive(workdir)
1751 _makedirsif(workdir)
1752 w = self.dovc(workdir, qw("initialize"))
1753 yield w; w.getResult()
1754 if not branch:
1755 rep = self.rep_trunk
1756 else:
1757 rep = os.path.join(self.darcs_base, branch)
1758 w = self.dovc(workdir, ["pull", "-a", rep])
1759 yield w; w.getResult()
1760 open(os.path.join(workdir, "subdir", "subdir.c"), "w").write(TRY_C)
1761 vc_try_checkout = deferredGenerator(vc_try_checkout)
1762
1763 def vc_try_finish(self, workdir):
1764 rmdirRecursive(workdir)
1765
1766
1767 class Darcs(VCBase, unittest.TestCase):
1768 vc_name = "darcs"
1769
1770 # Darcs has a metadir="_darcs", but it does not have an 'export'
1771 # mode
1772 metadir = None
1773 vctype = "source.Darcs"
1774 vctype_try = "darcs"
1775 has_got_revision = True
1776
1777 def testCheckout(self):
1778 self.helper.vcargs = { 'repourl': self.helper.rep_trunk }
1779 d = self.do_vctest(testRetry=False)
1780
1781 # TODO: testRetry has the same problem with Darcs as it does for
1782 # Arch
1783 return d
1784
1785 def testPatch(self):
1786 self.helper.vcargs = { 'baseURL': self.helper.darcs_base + "/",
1787 'defaultBranch': "trunk" }
1788 d = self.do_patch(PATCHLEVEL0)
1789 return d
1790
1791 def testPatchSubDir(self):
1792 self.helper.vcargs = { 'baseURL': self.helper.darcs_base + "/",
1793 'defaultBranch': "trunk" }
1794 d = self.do_patch(SUBDIR_ROOT)
1795 return d
1796
1797 def testPatchP2(self):
1798 self.helper.vcargs = { 'baseURL': self.helper.darcs_base + "/",
1799 'defaultBranch': "trunk" }
1800 d = self.do_patch(PATCHLEVEL2)
1801 return d
1802
1803 def testCheckoutBranch(self):
1804 self.helper.vcargs = { 'baseURL': self.helper.darcs_base + "/",
1805 'defaultBranch': "trunk" }
1806 d = self.do_branch()
1807 return d
1808
1809 def testCheckoutHTTP(self):
1810 self.serveHTTP()
1811 repourl = "http://localhost:%d/Darcs-Repository/trunk" % self.httpPort
1812 self.helper.vcargs = { 'repourl': repourl }
1813 d = self.do_vctest(testRetry=False)
1814 return d
1815
1816 def testTry(self):
1817 self.helper.vcargs = { 'baseURL': self.helper.darcs_base + "/",
1818 'defaultBranch': "trunk" }
1819 d = self.do_getpatch()
1820 return d
1821
1822 VCS.registerVC(Darcs.vc_name, DarcsHelper())
1823
1824
1825 class ArchCommon:
1826 def registerRepository(self, coordinates):
1827 a = self.archname
1828 w = self.dovc(self.repbase, "archives %s" % a)
1829 yield w; out = w.getResult()
1830 if out:
1831 w = self.dovc(self.repbase, "register-archive -d %s" % a)
1832 yield w; w.getResult()
1833 w = self.dovc(self.repbase, "register-archive %s" % coordinates)
1834 yield w; w.getResult()
1835 registerRepository = deferredGenerator(registerRepository)
1836
1837 def unregisterRepository(self):
1838 a = self.archname
1839 w = self.dovc(self.repbase, "archives %s" % a)
1840 yield w; out = w.getResult()
1841 if out:
1842 w = self.dovc(self.repbase, "register-archive -d %s" % a)
1843 yield w; out = w.getResult()
1844 unregisterRepository = deferredGenerator(unregisterRepository)
1845
1846 class TlaHelper(BaseHelper, ArchCommon):
1847 defaultbranch = "testvc--mainline--1"
1848 branchname = "testvc--branch--1"
1849 try_branchname = None # TlaExtractor can figure it out by itself
1850 archcmd = "tla"
1851
1852 def capable(self):
1853 tlapaths = which('tla')
1854 if not tlapaths:
1855 return (False, "Arch (tla) is not installed")
1856 self.vcexe = tlapaths[0]
1857 return (True, None)
1858
1859 def do_get(self, basedir, archive, branch, newdir):
1860 # the 'get' syntax is different between tla and baz. baz, while
1861 # claiming to honor an --archive argument, in fact ignores it. The
1862 # correct invocation is 'baz get archive/revision newdir'.
1863 if self.archcmd == "tla":
1864 w = self.dovc(basedir,
1865 "get -A %s %s %s" % (archive, branch, newdir))
1866 else:
1867 w = self.dovc(basedir,
1868 "get %s/%s %s" % (archive, branch, newdir))
1869 return w
1870
1871 def createRepository(self):
1872 self.createBasedir()
1873 # first check to see if bazaar is around, since we'll need to know
1874 # later
1875 d = VCS.capable(Bazaar.vc_name)
1876 d.addCallback(self._createRepository_1)
1877 return d
1878
1879 def _createRepository_1(self, res):
1880 has_baz = res[0]
1881
1882 # pick a hopefully unique string for the archive name, in the form
1883 # test-%d@buildbot.sf.net--testvc, since otherwise multiple copies of
1884 # the unit tests run in the same user account will collide (since the
1885 # archive names are kept in the per-user ~/.arch-params/ directory).
1886 pid = os.getpid()
1887 self.archname = "test-%s-%d@buildbot.sf.net--testvc" % (self.archcmd,
1888 pid)
1889 trunk = self.defaultbranch
1890 branch = self.branchname
1891
1892 repword = self.archcmd.capitalize()
1893 self.archrep = os.path.join(self.repbase, "%s-Repository" % repword)
1894 tmp = os.path.join(self.repbase, "archtmp")
1895 a = self.archname
1896
1897 self.populate(tmp)
1898
1899 w = self.dovc(tmp, "my-id", failureIsOk=True)
1900 yield w; res = w.getResult()
1901 if not res:
1902 # tla will fail a lot of operations if you have not set an ID
1903 w = self.do(tmp, [self.vcexe, "my-id",
1904 "Buildbot Test Suite <test@buildbot.sf.net>"])
1905 yield w; w.getResult()
1906
1907 if has_baz:
1908 # bazaar keeps a cache of revisions, but this test creates a new
1909 # archive each time it is run, so the cache causes errors.
1910 # Disable the cache to avoid these problems. This will be
1911 # slightly annoying for people who run the buildbot tests under
1912 # the same UID as one which uses baz on a regular basis, but
1913 # bazaar doesn't give us a way to disable the cache just for this
1914 # one archive.
1915 cmd = "%s cache-config --disable" % VCS.getHelper('bazaar').vcexe
1916 w = self.do(tmp, cmd)
1917 yield w; w.getResult()
1918
1919 w = waitForDeferred(self.unregisterRepository())
1920 yield w; w.getResult()
1921
1922 # these commands can be run in any directory
1923 w = self.dovc(tmp, "make-archive -l %s %s" % (a, self.archrep))
1924 yield w; w.getResult()
1925 if self.archcmd == "tla":
1926 w = self.dovc(tmp, "archive-setup -A %s %s" % (a, trunk))
1927 yield w; w.getResult()
1928 w = self.dovc(tmp, "archive-setup -A %s %s" % (a, branch))
1929 yield w; w.getResult()
1930 else:
1931 # baz does not require an 'archive-setup' step
1932 pass
1933
1934 # these commands must be run in the directory that is to be imported
1935 w = self.dovc(tmp, "init-tree --nested %s/%s" % (a, trunk))
1936 yield w; w.getResult()
1937 files = " ".join(["main.c", "version.c", "subdir",
1938 os.path.join("subdir", "subdir.c")])
1939 w = self.dovc(tmp, "add-id %s" % files)
1940 yield w; w.getResult()
1941
1942 w = self.dovc(tmp, "import %s/%s" % (a, trunk))
1943 yield w; out = w.getResult()
1944 self.addTrunkRev("base-0")
1945
1946 # create the branch
1947 if self.archcmd == "tla":
1948 branchstart = "%s--base-0" % trunk
1949 w = self.dovc(tmp, "tag -A %s %s %s" % (a, branchstart, branch))
1950 yield w; w.getResult()
1951 else:
1952 w = self.dovc(tmp, "branch %s" % branch)
1953 yield w; w.getResult()
1954
1955 rmdirRecursive(tmp)
1956
1957 # check out the branch
1958 w = self.do_get(self.repbase, a, branch, "archtmp")
1959 yield w; w.getResult()
1960 # and edit the file
1961 self.populate_branch(tmp)
1962 logfile = "++log.%s--%s" % (branch, a)
1963 logmsg = "Summary: commit on branch\nKeywords:\n\n"
1964 open(os.path.join(tmp, logfile), "w").write(logmsg)
1965 w = self.dovc(tmp, "commit")
1966 yield w; out = w.getResult()
1967 m = re.search(r'committed %s/%s--([\S]+)' % (a, branch),
1968 out)
1969 assert (m.group(1) == "base-0" or m.group(1).startswith("patch-"))
1970 self.addBranchRev(m.group(1))
1971
1972 w = waitForDeferred(self.unregisterRepository())
1973 yield w; w.getResult()
1974 rmdirRecursive(tmp)
1975
1976 # we unregister the repository each time, because we might have
1977 # changed the coordinates (since we switch from a file: URL to an
1978 # http: URL for various tests). The buildslave code doesn't forcibly
1979 # unregister the archive, so we have to do it here.
1980 w = waitForDeferred(self.unregisterRepository())
1981 yield w; w.getResult()
1982
1983 _createRepository_1 = deferredGenerator(_createRepository_1)
1984
1985 def vc_revise(self):
1986 # the fix needs to be done in a workspace that is linked to a
1987 # read-write version of the archive (i.e., using file-based
1988 # coordinates instead of HTTP ones), so we re-register the repository
1989 # before we begin. We unregister it when we're done to make sure the
1990 # build will re-register the correct one for whichever test is
1991 # currently being run.
1992
1993 # except, that source.Bazaar really doesn't like it when the archive
1994 # gets unregistered behind its back. The slave tries to do a 'baz
1995 # replay' in a tree with an archive that is no longer recognized, and
1996 # baz aborts with a botched invariant exception. This causes
1997 # mode=update to fall back to clobber+get, which flunks one of the
1998 # tests (the 'newfile' check in _do_vctest_update_3 fails)
1999
2000 # to avoid this, we take heroic steps here to leave the archive
2001 # registration in the same state as we found it.
2002
2003 tmp = os.path.join(self.repbase, "archtmp")
2004 a = self.archname
2005
2006 w = self.dovc(self.repbase, "archives %s" % a)
2007 yield w; out = w.getResult()
2008 assert out
2009 lines = out.split("\n")
2010 coordinates = lines[1].strip()
2011
2012 # now register the read-write location
2013 w = waitForDeferred(self.registerRepository(self.archrep))
2014 yield w; w.getResult()
2015
2016 trunk = self.defaultbranch
2017
2018 w = self.do_get(self.repbase, a, trunk, "archtmp")
2019 yield w; w.getResult()
2020
2021 # tla appears to use timestamps to determine which files have
2022 # changed, so wait long enough for the new file to have a different
2023 # timestamp
2024 time.sleep(2)
2025 self.version += 1
2026 version_c = VERSION_C % self.version
2027 open(os.path.join(tmp, "version.c"), "w").write(version_c)
2028
2029 logfile = "++log.%s--%s" % (trunk, a)
2030 logmsg = "Summary: revised_to_%d\nKeywords:\n\n" % self.version
2031 open(os.path.join(tmp, logfile), "w").write(logmsg)
2032 w = self.dovc(tmp, "commit")
2033 yield w; out = w.getResult()
2034 m = re.search(r'committed %s/%s--([\S]+)' % (a, trunk),
2035 out)
2036 assert (m.group(1) == "base-0" or m.group(1).startswith("patch-"))
2037 self.addTrunkRev(m.group(1))
2038
2039 # now re-register the original coordinates
2040 w = waitForDeferred(self.registerRepository(coordinates))
2041 yield w; w.getResult()
2042 rmdirRecursive(tmp)
2043 vc_revise = deferredGenerator(vc_revise)
2044
2045 def vc_try_checkout(self, workdir, rev, branch=None):
2046 assert os.path.abspath(workdir) == workdir
2047 if os.path.exists(workdir):
2048 rmdirRecursive(workdir)
2049
2050 a = self.archname
2051
2052 # register the read-write location, if it wasn't already registered
2053 w = waitForDeferred(self.registerRepository(self.archrep))
2054 yield w; w.getResult()
2055
2056 w = self.do_get(self.repbase, a, "testvc--mainline--1", workdir)
2057 yield w; w.getResult()
2058
2059 # timestamps. ick.
2060 time.sleep(2)
2061 open(os.path.join(workdir, "subdir", "subdir.c"), "w").write(TRY_C)
2062 vc_try_checkout = deferredGenerator(vc_try_checkout)
2063
2064 def vc_try_finish(self, workdir):
2065 rmdirRecursive(workdir)
2066
2067 class Arch(VCBase, unittest.TestCase):
2068 vc_name = "tla"
2069
2070 metadir = None
2071 # Arch has a metadir="{arch}", but it does not have an 'export' mode.
2072 vctype = "source.Arch"
2073 vctype_try = "tla"
2074 has_got_revision = True
2075
2076 def testCheckout(self):
2077 # these are the coordinates of the read-write archive used by all the
2078 # non-HTTP tests. testCheckoutHTTP overrides these.
2079 self.helper.vcargs = {'url': self.helper.archrep,
2080 'version': self.helper.defaultbranch }
2081 d = self.do_vctest(testRetry=False)
2082 # the current testRetry=True logic doesn't have the desired effect:
2083 # "update" is a no-op because arch knows that the repository hasn't
2084 # changed. Other VC systems will re-checkout missing files on
2085 # update, arch just leaves the tree untouched. TODO: come up with
2086 # some better test logic, probably involving a copy of the
2087 # repository that has a few changes checked in.
2088
2089 return d
2090
2091 def testCheckoutHTTP(self):
2092 self.serveHTTP()
2093 url = "http://localhost:%d/Tla-Repository" % self.httpPort
2094 self.helper.vcargs = { 'url': url,
2095 'version': "testvc--mainline--1" }
2096 d = self.do_vctest(testRetry=False)
2097 return d
2098
2099 def testPatch(self):
2100 self.helper.vcargs = {'url': self.helper.archrep,
2101 'version': self.helper.defaultbranch }
2102 d = self.do_patch(PATCHLEVEL0)
2103 return d
2104
2105 def testPatchSubDir(self):
2106 self.helper.vcargs = {'url': self.helper.archrep,
2107 'version': self.helper.defaultbranch }
2108 d = self.do_patch(SUBDIR_ROOT)
2109 return d
2110
2111 def testPatchP2(self):
2112 self.helper.vcargs = {'url': self.helper.archrep,
2113 'version': self.helper.defaultbranch }
2114 d = self.do_patch(PATCHLEVEL2)
2115 return d
2116
2117 def testCheckoutBranch(self):
2118 self.helper.vcargs = {'url': self.helper.archrep,
2119 'version': self.helper.defaultbranch }
2120 d = self.do_branch()
2121 return d
2122
2123 def testTry(self):
2124 self.helper.vcargs = {'url': self.helper.archrep,
2125 'version': self.helper.defaultbranch }
2126 d = self.do_getpatch()
2127 return d
2128
2129 VCS.registerVC(Arch.vc_name, TlaHelper())
2130
2131
2132 class BazaarHelper(TlaHelper):
2133 archcmd = "baz"
2134
2135 def capable(self):
2136 bazpaths = which('baz')
2137 if not bazpaths:
2138 return (False, "Arch (baz) is not installed")
2139 self.vcexe = bazpaths[0]
2140 return (True, None)
2141
2142 def setUp2(self, res):
2143 # we unregister the repository each time, because we might have
2144 # changed the coordinates (since we switch from a file: URL to an
2145 # http: URL for various tests). The buildslave code doesn't forcibly
2146 # unregister the archive, so we have to do it here.
2147 d = self.unregisterRepository()
2148 return d
2149
2150
2151 class Bazaar(Arch):
2152 vc_name = "bazaar"
2153
2154 vctype = "source.Bazaar"
2155 vctype_try = "baz"
2156 has_got_revision = True
2157
2158 fixtimer = None
2159
2160 def testCheckout(self):
2161 self.helper.vcargs = {'url': self.helper.archrep,
2162 # Baz adds the required 'archive' argument
2163 'archive': self.helper.archname,
2164 'version': self.helper.defaultbranch,
2165 }
2166 d = self.do_vctest(testRetry=False)
2167 # the current testRetry=True logic doesn't have the desired effect:
2168 # "update" is a no-op because arch knows that the repository hasn't
2169 # changed. Other VC systems will re-checkout missing files on
2170 # update, arch just leaves the tree untouched. TODO: come up with
2171 # some better test logic, probably involving a copy of the
2172 # repository that has a few changes checked in.
2173
2174 return d
2175
2176 def testCheckoutHTTP(self):
2177 self.serveHTTP()
2178 url = "http://localhost:%d/Baz-Repository" % self.httpPort
2179 self.helper.vcargs = { 'url': url,
2180 'archive': self.helper.archname,
2181 'version': self.helper.defaultbranch,
2182 }
2183 d = self.do_vctest(testRetry=False)
2184 return d
2185
2186 def testPatch(self):
2187 self.helper.vcargs = {'url': self.helper.archrep,
2188 # Baz adds the required 'archive' argument
2189 'archive': self.helper.archname,
2190 'version': self.helper.defaultbranch,
2191 }
2192 d = self.do_patch(PATCHLEVEL0)
2193 return d
2194
2195 def testPatchSubDir(self):
2196 self.helper.vcargs = {'url': self.helper.archrep,
2197 # Baz adds the required 'archive' argument
2198 'archive': self.helper.archname,
2199 'version': self.helper.defaultbranch,
2200 }
2201 d = self.do_patch(SUBDIR_ROOT)
2202 return d
2203
2204 def testPatchP2(self):
2205 self.helper.vcargs = {'url': self.helper.archrep,
2206 # Baz adds the required 'archive' argument
2207 'archive': self.helper.archname,
2208 'version': self.helper.defaultbranch,
2209 }
2210 d = self.do_patch(PATCHLEVEL2)
2211 return d
2212
2213 def testCheckoutBranch(self):
2214 self.helper.vcargs = {'url': self.helper.archrep,
2215 # Baz adds the required 'archive' argument
2216 'archive': self.helper.archname,
2217 'version': self.helper.defaultbranch,
2218 }
2219 d = self.do_branch()
2220 return d
2221
2222 def testTry(self):
2223 self.helper.vcargs = {'url': self.helper.archrep,
2224 # Baz adds the required 'archive' argument
2225 'archive': self.helper.archname,
2226 'version': self.helper.defaultbranch,
2227 }
2228 d = self.do_getpatch()
2229 return d
2230
2231 def fixRepository(self):
2232 self.fixtimer = None
2233 self.site.resource = self.root
2234
2235 def testRetry(self):
2236 # we want to verify that source.Source(retry=) works, and the easiest
2237 # way to make VC updates break (temporarily) is to break the HTTP
2238 # server that's providing the repository. Anything else pretty much
2239 # requires mutating the (read-only) BUILDBOT_TEST_VC repository, or
2240 # modifying the buildslave's checkout command while it's running.
2241
2242 # this test takes a while to run, so don't bother doing it with
2243 # anything other than baz
2244
2245 self.serveHTTP()
2246
2247 # break the repository server
2248 from twisted.web import static
2249 self.site.resource = static.Data("Sorry, repository is offline",
2250 "text/plain")
2251 # and arrange to fix it again in 5 seconds, while the test is
2252 # running.
2253 self.fixtimer = reactor.callLater(5, self.fixRepository)
2254
2255 url = "http://localhost:%d/Baz-Repository" % self.httpPort
2256 self.helper.vcargs = { 'url': url,
2257 'archive': self.helper.archname,
2258 'version': self.helper.defaultbranch,
2259 'retry': (5.0, 4),
2260 }
2261 d = self.do_vctest_once(True)
2262 d.addCallback(self._testRetry_1)
2263 return d
2264 def _testRetry_1(self, bs):
2265 # make sure there was mention of the retry attempt in the logs
2266 l = bs.getLogs()[0]
2267 self.failUnlessIn("unable to access URL", l.getText(),
2268 "funny, VC operation didn't fail at least once")
2269 self.failUnlessIn("update failed, trying 4 more times after 5 seconds",
2270 l.getTextWithHeaders(),
2271 "funny, VC operation wasn't reattempted")
2272
2273 def testRetryFails(self):
2274 # make sure that the build eventually gives up on a repository which
2275 # is completely unavailable
2276
2277 self.serveHTTP()
2278
2279 # break the repository server, and leave it broken
2280 from twisted.web import static
2281 self.site.resource = static.Data("Sorry, repository is offline",
2282 "text/plain")
2283
2284 url = "http://localhost:%d/Baz-Repository" % self.httpPort
2285 self.helper.vcargs = {'url': url,
2286 'archive': self.helper.archname,
2287 'version': self.helper.defaultbranch,
2288 'retry': (0.5, 3),
2289 }
2290 d = self.do_vctest_once(False)
2291 d.addCallback(self._testRetryFails_1)
2292 return d
2293 def _testRetryFails_1(self, bs):
2294 self.failUnlessEqual(bs.getResults(), FAILURE)
2295
2296 def tearDown2(self):
2297 if self.fixtimer:
2298 self.fixtimer.cancel()
2299 # tell tla to get rid of the leftover archive this test leaves in the
2300 # user's 'tla archives' listing. The name of this archive is provided
2301 # by the repository tarball, so the following command must use the
2302 # same name. We could use archive= to set it explicitly, but if you
2303 # change it from the default, then 'tla update' won't work.
2304 d = self.helper.unregisterRepository()
2305 return d
2306
2307 VCS.registerVC(Bazaar.vc_name, BazaarHelper())
2308
2309 class BzrHelper(BaseHelper):
2310 branchname = "branch"
2311 try_branchname = "branch"
2312
2313 def capable(self):
2314 bzrpaths = which('bzr')
2315 if not bzrpaths:
2316 return (False, "bzr is not installed")
2317 self.vcexe = bzrpaths[0]
2318 return (True, None)
2319
2320 def get_revision_number(self, out):
2321 for line in out.split("\n"):
2322 colon = line.index(":")
2323 key, value = line[:colon], line[colon+2:]
2324 if key == "revno":
2325 return int(value)
2326 raise RuntimeError("unable to find revno: in bzr output: '%s'" % out)
2327
2328 def createRepository(self):
2329 self.createBasedir()
2330 self.bzr_base = os.path.join(self.repbase, "Bzr-Repository")
2331 self.rep_trunk = os.path.join(self.bzr_base, "trunk")
2332 self.rep_branch = os.path.join(self.bzr_base, "branch")
2333 tmp = os.path.join(self.repbase, "bzrtmp")
2334 btmp = os.path.join(self.repbase, "bzrtmp-branch")
2335
2336 _makedirsif(self.rep_trunk)
2337 w = self.dovc(self.rep_trunk, ["init"])
2338 yield w; w.getResult()
2339 w = self.dovc(self.bzr_base,
2340 ["branch", self.rep_trunk, self.rep_branch])
2341 yield w; w.getResult()
2342
2343 w = self.dovc(self.repbase, ["checkout", self.rep_trunk, tmp])
2344 yield w; w.getResult()
2345 self.populate(tmp)
2346 w = self.dovc(tmp, qw("add"))
2347 yield w; w.getResult()
2348 w = self.dovc(tmp, qw("commit -m initial_import"))
2349 yield w; w.getResult()
2350 w = self.dovc(tmp, qw("version-info"))
2351 yield w; out = w.getResult()
2352 self.addTrunkRev(self.get_revision_number(out))
2353 rmdirRecursive(tmp)
2354
2355 # pull all trunk revisions to the branch
2356 w = self.dovc(self.rep_branch, qw("pull"))
2357 yield w; w.getResult()
2358 # obtain a branch tree
2359 w = self.dovc(self.repbase, ["checkout", self.rep_branch, btmp])
2360 yield w; w.getResult()
2361 # modify it
2362 self.populate_branch(btmp)
2363 w = self.dovc(btmp, qw("add"))
2364 yield w; w.getResult()
2365 w = self.dovc(btmp, qw("commit -m commit_on_branch"))
2366 yield w; w.getResult()
2367 w = self.dovc(btmp, qw("version-info"))
2368 yield w; out = w.getResult()
2369 self.addBranchRev(self.get_revision_number(out))
2370 rmdirRecursive(btmp)
2371 createRepository = deferredGenerator(createRepository)
2372
2373 def vc_revise(self):
2374 tmp = os.path.join(self.repbase, "bzrtmp")
2375 w = self.dovc(self.repbase, ["checkout", self.rep_trunk, tmp])
2376 yield w; w.getResult()
2377
2378 self.version += 1
2379 version_c = VERSION_C % self.version
2380 open(os.path.join(tmp, "version.c"), "w").write(version_c)
2381 w = self.dovc(tmp, qw("commit -m revised_to_%d" % self.version))
2382 yield w; w.getResult()
2383 w = self.dovc(tmp, qw("version-info"))
2384 yield w; out = w.getResult()
2385 self.addTrunkRev(self.get_revision_number(out))
2386 rmdirRecursive(tmp)
2387 vc_revise = deferredGenerator(vc_revise)
2388
2389 def vc_try_checkout(self, workdir, rev, branch=None):
2390 assert os.path.abspath(workdir) == workdir
2391 if os.path.exists(workdir):
2392 rmdirRecursive(workdir)
2393 #_makedirsif(workdir)
2394 if not branch:
2395 rep = self.rep_trunk
2396 else:
2397 rep = os.path.join(self.bzr_base, branch)
2398 w = self.dovc(self.bzr_base, ["clone", rep, workdir])
2399 yield w; w.getResult()
2400 open(os.path.join(workdir, "subdir", "subdir.c"), "w").write(TRY_C)
2401 vc_try_checkout = deferredGenerator(vc_try_checkout)
2402
2403 def vc_try_finish(self, workdir):
2404 rmdirRecursive(workdir)
2405
2406 class Bzr(VCBase, unittest.TestCase):
2407 vc_name = "bzr"
2408
2409 metadir = ".bzr"
2410 vctype = "source.Bzr"
2411 vctype_try = "bzr"
2412 has_got_revision = True
2413
2414 def testCheckout(self):
2415 self.helper.vcargs = { 'repourl': self.helper.rep_trunk,
2416 'forceSharedRepo': True}
2417 d = self.do_vctest(testRetry=False)
2418 # Check that the shared repository was created.
2419 def checkSharedRepo(res):
2420 shared_repo_path = os.path.join(self.slavebase, "vc-dir", ".bzr")
2421 assert os.path.exists(shared_repo_path)
2422 d.addCallback(checkSharedRepo)
2423
2424 # TODO: testRetry has the same problem with Bzr as it does for
2425 # Arch
2426 return d
2427 # Bzr is *slow*, and the testCheckout step takes a *very* long time
2428 testCheckout.timeout = 480
2429
2430 def testPatch(self):
2431 self.helper.vcargs = { 'baseURL': self.helper.bzr_base + "/",
2432 'defaultBranch': "trunk" }
2433 d = self.do_patch(PATCHLEVEL0)
2434 return d
2435
2436 def testPatchSubDir(self):
2437 self.helper.vcargs = { 'baseURL': self.helper.bzr_base + "/",
2438 'defaultBranch': "trunk" }
2439 d = self.do_patch(SUBDIR_ROOT)
2440 return d
2441
2442 def testPatchP2(self):
2443 self.helper.vcargs = { 'baseURL': self.helper.bzr_base + "/",
2444 'defaultBranch': "trunk" }
2445 d = self.do_patch(PATCHLEVEL2)
2446 return d
2447
2448 def testCheckoutBranch(self):
2449 self.helper.vcargs = { 'baseURL': self.helper.bzr_base + "/",
2450 'defaultBranch': "trunk" }
2451 d = self.do_branch()
2452 return d
2453
2454 def testCheckoutHTTP(self):
2455 self.serveHTTP()
2456 repourl = "http://localhost:%d/Bzr-Repository/trunk" % self.httpPort
2457 self.helper.vcargs = { 'repourl': repourl }
2458 d = self.do_vctest(testRetry=False)
2459 return d
2460
2461
2462 def fixRepository(self):
2463 self.fixtimer = None
2464 self.site.resource = self.root
2465
2466 def testRetry(self):
2467 # this test takes a while to run
2468 self.serveHTTP()
2469
2470 # break the repository server
2471 from twisted.web import static
2472 self.site.resource = static.Data("Sorry, repository is offline",
2473 "text/plain")
2474 # and arrange to fix it again in 5 seconds, while the test is
2475 # running.
2476 self.fixtimer = reactor.callLater(5, self.fixRepository)
2477
2478 repourl = "http://localhost:%d/Bzr-Repository/trunk" % self.httpPort
2479 self.helper.vcargs = { 'repourl': repourl,
2480 'retry': (5.0, 4),
2481 }
2482 d = self.do_vctest_once(True)
2483 d.addCallback(self._testRetry_1)
2484 return d
2485 def _testRetry_1(self, bs):
2486 # make sure there was mention of the retry attempt in the logs
2487 l = bs.getLogs()[0]
2488 self.failUnlessIn("ERROR: Not a branch: ", l.getText(),
2489 "funny, VC operation didn't fail at least once")
2490 self.failUnlessIn("update failed, trying 4 more times after 5 seconds",
2491 l.getTextWithHeaders(),
2492 "funny, VC operation wasn't reattempted")
2493
2494 def testRetryFails(self):
2495 # make sure that the build eventually gives up on a repository which
2496 # is completely unavailable
2497
2498 self.serveHTTP()
2499
2500 # break the repository server, and leave it broken
2501 from twisted.web import static
2502 self.site.resource = static.Data("Sorry, repository is offline",
2503 "text/plain")
2504
2505 repourl = "http://localhost:%d/Bzr-Repository/trunk" % self.httpPort
2506 self.helper.vcargs = { 'repourl': repourl,
2507 'retry': (0.5, 3),
2508 }
2509 d = self.do_vctest_once(False)
2510 d.addCallback(self._testRetryFails_1)
2511 return d
2512 def _testRetryFails_1(self, bs):
2513 self.failUnlessEqual(bs.getResults(), FAILURE)
2514
2515
2516 def testTry(self):
2517 self.helper.vcargs = { 'baseURL': self.helper.bzr_base + "/",
2518 'defaultBranch': "trunk" }
2519 d = self.do_getpatch()
2520 return d
2521
2522 VCS.registerVC(Bzr.vc_name, BzrHelper())
2523
2524
2525 class MercurialHelper(BaseHelper):
2526 branchname = "branch"
2527 try_branchname = "branch"
2528
2529 def capable(self):
2530 hgpaths = which("hg")
2531 if not hgpaths:
2532 return (False, "Mercurial is not installed")
2533 self.vcexe = hgpaths[0]
2534 return (True, None)
2535
2536 def extract_id(self, output):
2537 m = re.search(r'^(\w+)', output)
2538 return m.group(0)
2539
2540 def createRepository(self):
2541 self.createBasedir()
2542 self.hg_base = os.path.join(self.repbase, "Mercurial-Repository")
2543 self.rep_trunk = os.path.join(self.hg_base, "trunk")
2544 self.rep_branch = os.path.join(self.hg_base, "branch")
2545 tmp = os.path.join(self.hg_base, "hgtmp")
2546
2547 _makedirsif(self.rep_trunk)
2548 w = self.dovc(self.rep_trunk, "init")
2549 yield w; w.getResult()
2550 _makedirsif(self.rep_branch)
2551 w = self.dovc(self.rep_branch, "init")
2552 yield w; w.getResult()
2553
2554 self.populate(tmp)
2555 w = self.dovc(tmp, "init")
2556 yield w; w.getResult()
2557 w = self.dovc(tmp, "add")
2558 yield w; w.getResult()
2559 w = self.dovc(tmp, ['commit', '-m', 'initial_import', '--user' ,'bbtests @localhost' ])
2560 yield w; w.getResult()
2561 w = self.dovc(tmp, ['push', self.rep_trunk])
2562 # note that hg-push does not actually update the working directory
2563 yield w; w.getResult()
2564 w = self.dovc(tmp, "identify")
2565 yield w; out = w.getResult()
2566 self.addTrunkRev(self.extract_id(out))
2567
2568 self.populate_branch(tmp)
2569 w = self.dovc(tmp, ['commit', '-m', 'commit_on_branch', '--user' ,'bbtes ts@localhost' ])
2570 yield w; w.getResult()
2571 w = self.dovc(tmp, ['push', self.rep_branch])
2572 yield w; w.getResult()
2573 w = self.dovc(tmp, "identify")
2574 yield w; out = w.getResult()
2575 self.addBranchRev(self.extract_id(out))
2576 rmdirRecursive(tmp)
2577 createRepository = deferredGenerator(createRepository)
2578
2579 def vc_revise(self):
2580 tmp = os.path.join(self.hg_base, "hgtmp2")
2581 w = self.dovc(self.hg_base, ['clone', self.rep_trunk, tmp])
2582 yield w; w.getResult()
2583
2584 self.version += 1
2585 version_c = VERSION_C % self.version
2586 version_c_filename = os.path.join(tmp, "version.c")
2587 open(version_c_filename, "w").write(version_c)
2588 # hg uses timestamps to distinguish files which have changed, so we
2589 # force the mtime forward a little bit
2590 future = time.time() + 2*self.version
2591 os.utime(version_c_filename, (future, future))
2592 w = self.dovc(tmp, ['commit', '-m', 'revised_to_%d' % self.version, '--u ser' ,'bbtests@localhost' ])
2593 yield w; w.getResult()
2594 w = self.dovc(tmp, ['push', self.rep_trunk])
2595 yield w; w.getResult()
2596 w = self.dovc(tmp, "identify")
2597 yield w; out = w.getResult()
2598 self.addTrunkRev(self.extract_id(out))
2599 rmdirRecursive(tmp)
2600 vc_revise = deferredGenerator(vc_revise)
2601
2602 def vc_try_checkout(self, workdir, rev, branch=None):
2603 assert os.path.abspath(workdir) == workdir
2604 if os.path.exists(workdir):
2605 rmdirRecursive(workdir)
2606 if branch:
2607 src = self.rep_branch
2608 else:
2609 src = self.rep_trunk
2610 w = self.dovc(self.hg_base, ['clone', src, workdir])
2611 yield w; w.getResult()
2612 try_c_filename = os.path.join(workdir, "subdir", "subdir.c")
2613 open(try_c_filename, "w").write(TRY_C)
2614 future = time.time() + 2*self.version
2615 os.utime(try_c_filename, (future, future))
2616 vc_try_checkout = deferredGenerator(vc_try_checkout)
2617
2618 def vc_try_finish(self, workdir):
2619 rmdirRecursive(workdir)
2620
2621 class MercurialServerPP(protocol.ProcessProtocol):
2622 def __init__(self):
2623 self.wait = defer.Deferred()
2624
2625 def outReceived(self, data):
2626 log.msg("hg-serve-stdout: %s" % (data,))
2627 def errReceived(self, data):
2628 print "HG-SERVE-STDERR:", data
2629 log.msg("hg-serve-stderr: %s" % (data,))
2630 def processEnded(self, reason):
2631 log.msg("hg-serve ended: %s" % reason)
2632 self.wait.callback(None)
2633
2634
2635 class Mercurial(VCBase, unittest.TestCase):
2636 vc_name = "hg"
2637
2638 # Mercurial has a metadir=".hg", but it does not have an 'export' mode.
2639 metadir = None
2640 vctype = "source.Mercurial"
2641 vctype_try = "hg"
2642 has_got_revision = True
2643 _hg_server = None
2644 _wait_for_server_poller = None
2645 _pp = None
2646
2647 # make sure we can identify source repo via default path
2648 def _testSourceURLPresent(self, rc):
2649 d = self.helper.dovc(self.workdir, ['paths', 'default'])
2650 yield d; d.getResult()
2651 _testSourceURLPresent = deferredGenerator(_testSourceURLPresent)
2652
2653 def testCheckout(self):
2654 self.helper.vcargs = { 'repourl': self.helper.rep_trunk }
2655 d = self.do_vctest(testRetry=False)
2656
2657 # TODO: testRetry has the same problem with Mercurial as it does for
2658 # Arch
2659 d.addCallback(self._testSourceURLPresent)
2660 return d
2661
2662 def testPatch(self):
2663 self.helper.vcargs = { 'baseURL': self.helper.hg_base + "/",
2664 'defaultBranch': "trunk" }
2665 d = self.do_patch(PATCHLEVEL0)
2666 return d
2667
2668 def testPatchSubDir(self):
2669 self.helper.vcargs = { 'baseURL': self.helper.hg_base + "/",
2670 'defaultBranch': "trunk" }
2671 d = self.do_patch(SUBDIR_ROOT)
2672 return d
2673
2674 def testPatchP2(self):
2675 self.helper.vcargs = { 'baseURL': self.helper.hg_base + "/",
2676 'defaultBranch': "trunk" }
2677 d = self.do_patch(PATCHLEVEL2)
2678 return d
2679
2680 def testCheckoutBranch(self):
2681 self.helper.vcargs = { 'baseURL': self.helper.hg_base + "/",
2682 'defaultBranch': "trunk" }
2683
2684 d = self.do_branch()
2685 d.addCallback(self._testSourceURLPresent)
2686 return d
2687
2688 def serveHTTP(self):
2689 # the easiest way to publish hg over HTTP is by running 'hg serve' as
2690 # a child process while the test is running. (you can also use a CGI
2691 # script, which sounds difficult, or you can publish the files
2692 # directly, which isn't well documented).
2693
2694 # grr.. 'hg serve' doesn't let you use --port=0 to mean "pick a free
2695 # port", instead it uses it as a signal to use the default (port
2696 # 8000). This means there is no way to make it choose a free port, so
2697 # we are forced to make it use a statically-defined one, making it
2698 # harder to avoid collisions.
2699 self.httpPort = 8300 + (os.getpid() % 200)
2700 args = [self.helper.vcexe,
2701 "serve", "--port", str(self.httpPort), "--verbose"]
2702
2703 # in addition, hg doesn't flush its stdout, so we can't wait for the
2704 # "listening at" message to know when it's safe to start the test.
2705 # Instead, poll every second until a getPage works.
2706
2707 self._pp = MercurialServerPP() # logs+discards everything
2708
2709 # this serves one tree at a time, so we serve trunk. TODO: test hg's
2710 # in-repo branches, for which a single tree will hold all branches.
2711 self._hg_server = reactor.spawnProcess(self._pp, self.helper.vcexe, args ,
2712 os.environ,
2713 self.helper.rep_trunk)
2714 log.msg("waiting for hg serve to start")
2715 done_d = defer.Deferred()
2716 def poll():
2717 d = client.getPage("http://localhost:%d/" % self.httpPort)
2718 def success(res):
2719 log.msg("hg serve appears to have started")
2720 self._wait_for_server_poller.stop()
2721 done_d.callback(None)
2722 def ignore_connection_refused(f):
2723 f.trap(error.ConnectionRefusedError)
2724 d.addCallbacks(success, ignore_connection_refused)
2725 d.addErrback(done_d.errback)
2726 return d
2727 self._wait_for_server_poller = task.LoopingCall(poll)
2728 self._wait_for_server_poller.start(0.5, True)
2729 return done_d
2730
2731 def tearDown(self):
2732 if self._wait_for_server_poller:
2733 if self._wait_for_server_poller.running:
2734 self._wait_for_server_poller.stop()
2735 if self._hg_server:
2736 self._hg_server.loseConnection()
2737 try:
2738 self._hg_server.signalProcess("KILL")
2739 except error.ProcessExitedAlready:
2740 pass
2741 self._hg_server = None
2742 return VCBase.tearDown(self)
2743
2744 def tearDown2(self):
2745 if self._pp:
2746 return self._pp.wait
2747
2748 def testCheckoutHTTP(self):
2749 d = self.serveHTTP()
2750 def _started(res):
2751 repourl = "http://localhost:%d/" % self.httpPort
2752 self.helper.vcargs = { 'repourl': repourl }
2753 return self.do_vctest(testRetry=False)
2754 d.addCallback(_started)
2755 d.addCallback(self._testSourceURLPresent)
2756 return d
2757
2758 def testTry(self):
2759 self.helper.vcargs = { 'baseURL': self.helper.hg_base + "/",
2760 'defaultBranch': "trunk" }
2761 d = self.do_getpatch()
2762 return d
2763
2764 VCS.registerVC(Mercurial.vc_name, MercurialHelper())
2765
2766 class MercurialInRepoHelper(MercurialHelper):
2767 branchname = "the_branch"
2768 try_branchname = "the_branch"
2769
2770
2771 def createRepository(self):
2772 self.createBasedir()
2773 self.hg_base = os.path.join(self.repbase, "Mercurial-Repository")
2774 self.repo = os.path.join(self.hg_base, "inrepobranch")
2775 tmp = os.path.join(self.hg_base, "hgtmp")
2776
2777 _makedirsif(self.repo)
2778 w = self.dovc(self.repo, "init")
2779 yield w; w.getResult()
2780
2781 self.populate(tmp)
2782 w = self.dovc(tmp, "init")
2783 yield w; w.getResult()
2784 w = self.dovc(tmp, "add")
2785 yield w; w.getResult()
2786 w = self.dovc(tmp, ['commit', '-m', 'initial_import'])
2787 yield w; w.getResult()
2788 w = self.dovc(tmp, ['push', self.repo])
2789 # note that hg-push does not actually update the working directory
2790 yield w; w.getResult()
2791 w = self.dovc(tmp, "identify")
2792 yield w; out = w.getResult()
2793 self.addTrunkRev(self.extract_id(out))
2794
2795 self.populate_branch(tmp)
2796 w = self.dovc(tmp, ['branch', self.branchname])
2797 yield w; w.getResult()
2798 w = self.dovc(tmp, ['commit', '-m', 'commit_on_branch'])
2799 yield w; w.getResult()
2800 w = self.dovc(tmp, ['push', '-f', self.repo])
2801 yield w; w.getResult()
2802 w = self.dovc(tmp, "identify")
2803 yield w; out = w.getResult()
2804 self.addBranchRev(self.extract_id(out))
2805 rmdirRecursive(tmp)
2806 createRepository = deferredGenerator(createRepository)
2807
2808 def vc_revise(self):
2809 tmp = os.path.join(self.hg_base, "hgtmp2")
2810 w = self.dovc(self.hg_base, ['clone', self.repo, tmp])
2811 yield w; w.getResult()
2812 w = self.dovc(tmp, ['update', '--clean', '--rev', 'default'])
2813 yield w; w.getResult()
2814
2815 self.version += 1
2816 version_c = VERSION_C % self.version
2817 version_c_filename = os.path.join(tmp, "version.c")
2818 open(version_c_filename, "w").write(version_c)
2819 # hg uses timestamps to distinguish files which have changed, so we
2820 # force the mtime forward a little bit
2821 future = time.time() + 2*self.version
2822 os.utime(version_c_filename, (future, future))
2823 w = self.dovc(tmp, ['commit', '-m', 'revised_to_%d' % self.version])
2824 yield w; w.getResult()
2825 w = self.dovc(tmp, ['push', '--force', self.repo])
2826 yield w; w.getResult()
2827 w = self.dovc(tmp, "identify")
2828 yield w; out = w.getResult()
2829 self.addTrunkRev(self.extract_id(out))
2830 rmdirRecursive(tmp)
2831 vc_revise = deferredGenerator(vc_revise)
2832
2833 def vc_try_checkout(self, workdir, rev, branch=None):
2834 assert os.path.abspath(workdir) == workdir
2835 if os.path.exists(workdir):
2836 rmdirRecursive(workdir)
2837 w = self.dovc(self.hg_base, ['clone', self.repo, workdir])
2838 yield w; w.getResult()
2839 if not branch: branch = "default"
2840 w = self.dovc(workdir, ['update', '--clean', '--rev', branch ])
2841 yield w; w.getResult()
2842
2843 try_c_filename = os.path.join(workdir, "subdir", "subdir.c")
2844 open(try_c_filename, "w").write(TRY_C)
2845 future = time.time() + 2*self.version
2846 os.utime(try_c_filename, (future, future))
2847 vc_try_checkout = deferredGenerator(vc_try_checkout)
2848
2849 def vc_try_finish(self, workdir):
2850 rmdirRecursive(workdir)
2851 pass
2852
2853
2854 class MercurialInRepo(Mercurial):
2855 vc_name = 'MercurialInRepo'
2856
2857 def default_args(self):
2858 return { 'repourl': self.helper.repo,
2859 'branchType': 'inrepo',
2860 'defaultBranch': 'default' }
2861
2862 def testCheckout(self):
2863 self.helper.vcargs = self.default_args()
2864 d = self.do_vctest(testRetry=False)
2865
2866 # TODO: testRetry has the same problem with Mercurial as it does for
2867 # Arch
2868 d.addCallback(self._testSourceURLPresent)
2869 return d
2870
2871 def testPatch(self):
2872 self.helper.vcargs = self.default_args()
2873 d = self.do_patch(PATCHLEVEL0)
2874 return d
2875
2876 def testPatchSubDir(self):
2877 self.helper.vcargs = self.default_args()
2878 d = self.do_patch(SUBDIR_ROOT)
2879 return d
2880
2881 def testPatchP2(self):
2882 self.helper.vcargs = self.default_args()
2883 d = self.do_patch(PATCHLEVEL2)
2884 return d
2885
2886 def testCheckoutBranch(self):
2887 self.helper.vcargs = self.default_args()
2888 d = self.do_branch()
2889 d.addCallback(self._testSourceURLPresent)
2890 return d
2891
2892 def serveHTTP(self):
2893 # the easiest way to publish hg over HTTP is by running 'hg serve' as
2894 # a child process while the test is running. (you can also use a CGI
2895 # script, which sounds difficult, or you can publish the files
2896 # directly, which isn't well documented).
2897
2898 # grr.. 'hg serve' doesn't let you use --port=0 to mean "pick a free
2899 # port", instead it uses it as a signal to use the default (port
2900 # 8000). This means there is no way to make it choose a free port, so
2901 # we are forced to make it use a statically-defined one, making it
2902 # harder to avoid collisions.
2903 self.httpPort = 8300 + (os.getpid() % 200)
2904 args = [self.helper.vcexe,
2905 "serve", "--port", str(self.httpPort), "--verbose"]
2906
2907 # in addition, hg doesn't flush its stdout, so we can't wait for the
2908 # "listening at" message to know when it's safe to start the test.
2909 # Instead, poll every second until a getPage works.
2910
2911 self._pp = MercurialServerPP() # logs+discards everything
2912 # this serves one tree at a time, so we serve trunk. TODO: test hg's
2913 # in-repo branches, for which a single tree will hold all branches.
2914 self._hg_server = reactor.spawnProcess(self._pp, self.helper.vcexe, args ,
2915 os.environ,
2916 self.helper.repo)
2917 log.msg("waiting for hg serve to start")
2918 done_d = defer.Deferred()
2919 def poll():
2920 d = client.getPage("http://localhost:%d/" % self.httpPort)
2921 def success(res):
2922 log.msg("hg serve appears to have started")
2923 self._wait_for_server_poller.stop()
2924 done_d.callback(None)
2925 def ignore_connection_refused(f):
2926 f.trap(error.ConnectionRefusedError)
2927 d.addCallbacks(success, ignore_connection_refused)
2928 d.addErrback(done_d.errback)
2929 return d
2930 self._wait_for_server_poller = task.LoopingCall(poll)
2931 self._wait_for_server_poller.start(0.5, True)
2932 return done_d
2933
2934 def tearDown(self):
2935 if self._wait_for_server_poller:
2936 if self._wait_for_server_poller.running:
2937 self._wait_for_server_poller.stop()
2938 if self._hg_server:
2939 self._hg_server.loseConnection()
2940 try:
2941 self._hg_server.signalProcess("KILL")
2942 except error.ProcessExitedAlready:
2943 pass
2944 self._hg_server = None
2945 return VCBase.tearDown(self)
2946
2947 def tearDown2(self):
2948 if self._pp:
2949 return self._pp.wait
2950
2951 def testCheckoutHTTP(self):
2952 d = self.serveHTTP()
2953 def _started(res):
2954 repourl = "http://localhost:%d/" % self.httpPort
2955 self.helper.vcargs = self.default_args()
2956 self.helper.vcargs['repourl'] = repourl
2957 return self.do_vctest(testRetry=False)
2958 d.addCallback(_started)
2959 d.addCallback(self._testSourceURLPresent)
2960 return d
2961
2962 def testTry(self):
2963 self.helper.vcargs = self.default_args()
2964 d = self.do_getpatch()
2965 return d
2966
2967 VCS.registerVC(MercurialInRepo.vc_name, MercurialInRepoHelper())
2968
2969
2970 class GitHelper(BaseHelper):
2971 branchname = "branch"
2972 try_branchname = "branch"
2973
2974 def capable(self):
2975 gitpaths = which('git')
2976 if not gitpaths:
2977 return (False, "GIT is not installed")
2978 d = utils.getProcessOutput(gitpaths[0], ["--version"], env=os.environ)
2979 d.addCallback(self._capable, gitpaths[0])
2980 return d
2981
2982 def _capable(self, v, vcexe):
2983 try:
2984 m = re.search(r'\b(\d+)\.(\d+)', v)
2985
2986 if not m:
2987 raise Exception, 'no regex match'
2988
2989 ver = tuple([int(num) for num in m.groups()])
2990
2991 # git-1.1.3 (as shipped with Dapper) doesn't understand 'git
2992 # init' (it wants 'git init-db'), and fails unit tests that
2993 # involve branches. git-1.5.3.6 (on my debian/unstable system)
2994 # works. I don't know where the dividing point is: if someone can
2995 # figure it out (or figure out how to make buildbot support more
2996 # versions), please update this check.
2997 if ver < (1, 2):
2998 return (False, "Found git (%s) but it is older than 1.2.x" % vce xe)
2999
3000 except Exception, e:
3001 log.msg("couldn't identify git version number in output:")
3002 log.msg("'''%s'''" % v)
3003 log.msg("because: %s" % e)
3004 log.msg("skipping tests")
3005 return (False,
3006 "Found git (%s) but couldn't identify its version from '%s'" % (vcexe, v))
3007
3008 self.vcexe = vcexe
3009 return (True, None)
3010
3011 def createRepository(self):
3012 self.createBasedir()
3013 self.gitrepo = os.path.join(self.repbase,
3014 "GIT-Repository")
3015 tmp = os.path.join(self.repbase, "gittmp")
3016
3017 env = os.environ.copy()
3018 env['GIT_DIR'] = self.gitrepo
3019 w = self.dovc(self.repbase, "init", env=env)
3020 yield w; w.getResult()
3021
3022 # NetBSD pkgsrc uses templates that stupidly enable the update hook, req uiring
3023 # a non-default description. This is broken, but easily worked around.
3024 # http://www.netbsd.org/cgi-bin/query-pr-single.pl?number=41683
3025 descrfile = os.path.join(self.gitrepo, "description")
3026 open(descrfile, "w").write("NetBSD workaround")
3027
3028 self.populate(tmp)
3029 w = self.dovc(tmp, "init")
3030 yield w; w.getResult()
3031 w = self.dovc(tmp, ["add", "."])
3032 yield w; w.getResult()
3033 w = self.dovc(tmp, ["config", "user.email", "buildbot-trial@localhost"])
3034 yield w; w.getResult()
3035 w = self.dovc(tmp, ["config", "user.name", "Buildbot Trial"])
3036 yield w; w.getResult()
3037 w = self.dovc(tmp, ["commit", "-m", "initial_import"])
3038 yield w; w.getResult()
3039
3040 w = self.dovc(tmp, ["checkout", "-b", self.branchname])
3041 yield w; w.getResult()
3042 self.populate_branch(tmp)
3043 w = self.dovc(tmp, ["commit", "-a", "-m", "commit_on_branch"])
3044 yield w; w.getResult()
3045
3046 w = self.dovc(tmp, ["rev-parse", "master", self.branchname])
3047 yield w; out = w.getResult()
3048 revs = out.splitlines()
3049 self.addTrunkRev(revs[0])
3050 self.addBranchRev(revs[1])
3051
3052 w = self.dovc(tmp, ["push", self.gitrepo, "master", self.branchname])
3053 yield w; w.getResult()
3054
3055 rmdirRecursive(tmp)
3056 createRepository = deferredGenerator(createRepository)
3057
3058 def vc_revise(self):
3059 tmp = os.path.join(self.repbase, "gittmp")
3060 rmdirRecursive(tmp)
3061 log.msg("vc_revise" + self.gitrepo)
3062 w = self.dovc(self.repbase, ["clone", self.gitrepo, "gittmp"])
3063 yield w; w.getResult()
3064 w = self.dovc(tmp, ["config", "user.email", "buildbot-trial@localhost"])
3065 yield w; w.getResult()
3066 w = self.dovc(tmp, ["config", "user.name", "Buildbot Trial"])
3067 yield w; w.getResult()
3068
3069 self.version += 1
3070 version_c = VERSION_C % self.version
3071 open(os.path.join(tmp, "version.c"), "w").write(version_c)
3072
3073 w = self.dovc(tmp, ["commit", "-m", "revised_to_%d" % self.version,
3074 "version.c"])
3075 yield w; w.getResult()
3076 w = self.dovc(tmp, ["rev-parse", "master"])
3077 yield w; out = w.getResult()
3078 self.addTrunkRev(out.strip())
3079
3080 w = self.dovc(tmp, ["push", self.gitrepo, "master"])
3081 yield w; out = w.getResult()
3082 rmdirRecursive(tmp)
3083 vc_revise = deferredGenerator(vc_revise)
3084
3085 def vc_try_checkout(self, workdir, rev, branch=None):
3086 assert os.path.abspath(workdir) == workdir
3087 if os.path.exists(workdir):
3088 rmdirRecursive(workdir)
3089
3090 w = self.dovc(self.repbase, ["clone", self.gitrepo, workdir])
3091 yield w; w.getResult()
3092 w = self.dovc(workdir, ["config", "user.email", "buildbot-trial@localhos t"])
3093 yield w; w.getResult()
3094 w = self.dovc(workdir, ["config", "user.name", "Buildbot Trial"])
3095 yield w; w.getResult()
3096
3097 if branch is not None:
3098 w = self.dovc(workdir, ["checkout", "-b", branch,
3099 "origin/%s" % branch])
3100 yield w; w.getResult()
3101
3102 # Hmm...why do nobody else bother to check out the correct
3103 # revision?
3104 w = self.dovc(workdir, ["reset", "--hard", rev])
3105 yield w; w.getResult()
3106
3107 try_c_filename = os.path.join(workdir, "subdir", "subdir.c")
3108 open(try_c_filename, "w").write(TRY_C)
3109 vc_try_checkout = deferredGenerator(vc_try_checkout)
3110
3111 def vc_try_finish(self, workdir):
3112 rmdirRecursive(workdir)
3113
3114 class Git(VCBase, unittest.TestCase):
3115 vc_name = "git"
3116
3117 # No 'export' mode yet...
3118 # metadir = ".git"
3119 vctype = "source.Git"
3120 vctype_try = "git"
3121 has_got_revision = True
3122
3123 def testCheckout(self):
3124 self.helper.vcargs = { 'repourl': self.helper.gitrepo }
3125 d = self.do_vctest()
3126 return d
3127
3128 def testPatch(self):
3129 self.helper.vcargs = { 'repourl': self.helper.gitrepo,
3130 'branch': "master" }
3131 d = self.do_patch(PATCHLEVEL0)
3132 return d
3133
3134 def testPatchSubDir(self):
3135 self.helper.vcargs = { 'repourl': self.helper.gitrepo,
3136 'branch': "master" }
3137 d = self.do_patch(SUBDIR_ROOT)
3138 return d
3139
3140 def testPatchP2(self):
3141 self.helper.vcargs = { 'repourl': self.helper.gitrepo,
3142 'branch': "master" }
3143 d = self.do_patch(PATCHLEVEL2)
3144 return d
3145
3146 def testCheckoutBranch(self):
3147 self.helper.vcargs = { 'repourl': self.helper.gitrepo,
3148 'branch': "master" }
3149 d = self.do_branch()
3150 return d
3151
3152 def testTry(self):
3153 self.helper.vcargs = { 'repourl': self.helper.gitrepo,
3154 'branch': "master" }
3155 d = self.do_getpatch()
3156 return d
3157
3158 VCS.registerVC(Git.vc_name, GitHelper())
3159
3160
3161 class Sources(unittest.TestCase):
3162 # TODO: this needs serious rethink
3163 def makeChange(self, when=None, revision=None):
3164 if when:
3165 when = mktime_tz(parsedate_tz(when))
3166 return changes.Change("fred", [], "", when=when, revision=revision)
3167
3168 def testCVS1(self):
3169 r = base.BuildRequest("forced build", SourceStamp(), 'test_builder')
3170 b = base.Build([r])
3171 s = source.CVS(cvsroot=None, cvsmodule=None)
3172 s.setBuild(b)
3173 self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), None)
3174
3175 def testCVS2(self):
3176 c = []
3177 c.append(self.makeChange("Wed, 08 Sep 2004 09:00:00 -0700"))
3178 c.append(self.makeChange("Wed, 08 Sep 2004 09:01:00 -0700"))
3179 c.append(self.makeChange("Wed, 08 Sep 2004 09:02:00 -0700"))
3180 r = base.BuildRequest("forced", SourceStamp(changes=c), 'test_builder')
3181 submitted = "Wed, 08 Sep 2004 09:04:00 -0700"
3182 r.submittedAt = mktime_tz(parsedate_tz(submitted))
3183 b = base.Build([r])
3184 s = source.CVS(cvsroot=None, cvsmodule=None)
3185 s.setBuild(b)
3186 self.failUnlessEqual(s.computeSourceRevision(b.allChanges()),
3187 "Wed, 08 Sep 2004 16:03:00 -0000")
3188
3189 def testCVS3(self):
3190 c = []
3191 c.append(self.makeChange("Wed, 08 Sep 2004 09:00:00 -0700"))
3192 c.append(self.makeChange("Wed, 08 Sep 2004 09:01:00 -0700"))
3193 c.append(self.makeChange("Wed, 08 Sep 2004 09:02:00 -0700"))
3194 r = base.BuildRequest("forced", SourceStamp(changes=c), 'test_builder')
3195 submitted = "Wed, 08 Sep 2004 09:04:00 -0700"
3196 r.submittedAt = mktime_tz(parsedate_tz(submitted))
3197 b = base.Build([r])
3198 s = source.CVS(cvsroot=None, cvsmodule=None, checkoutDelay=10)
3199 s.setBuild(b)
3200 self.failUnlessEqual(s.computeSourceRevision(b.allChanges()),
3201 "Wed, 08 Sep 2004 16:02:10 -0000")
3202
3203 def testCVS4(self):
3204 c = []
3205 c.append(self.makeChange("Wed, 08 Sep 2004 09:00:00 -0700"))
3206 c.append(self.makeChange("Wed, 08 Sep 2004 09:01:00 -0700"))
3207 c.append(self.makeChange("Wed, 08 Sep 2004 09:02:00 -0700"))
3208 r1 = base.BuildRequest("forced", SourceStamp(changes=c), 'test_builder')
3209 submitted = "Wed, 08 Sep 2004 09:04:00 -0700"
3210 r1.submittedAt = mktime_tz(parsedate_tz(submitted))
3211
3212 c = []
3213 c.append(self.makeChange("Wed, 08 Sep 2004 09:05:00 -0700"))
3214 r2 = base.BuildRequest("forced", SourceStamp(changes=c), 'test_builder')
3215 submitted = "Wed, 08 Sep 2004 09:07:00 -0700"
3216 r2.submittedAt = mktime_tz(parsedate_tz(submitted))
3217
3218 b = base.Build([r1, r2])
3219 s = source.CVS(cvsroot=None, cvsmodule=None)
3220 s.setBuild(b)
3221 self.failUnlessEqual(s.computeSourceRevision(b.allChanges()),
3222 "Wed, 08 Sep 2004 16:06:00 -0000")
3223
3224 def testSVN1(self):
3225 r = base.BuildRequest("forced", SourceStamp(), 'test_builder')
3226 b = base.Build([r])
3227 s = source.SVN(svnurl="dummy")
3228 s.setBuild(b)
3229 self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), None)
3230
3231 def testSVN2(self):
3232 c = []
3233 c.append(self.makeChange(revision=4))
3234 c.append(self.makeChange(revision=10))
3235 c.append(self.makeChange(revision=67))
3236 r = base.BuildRequest("forced", SourceStamp(changes=c), 'test_builder')
3237 b = base.Build([r])
3238 s = source.SVN(svnurl="dummy")
3239 s.setBuild(b)
3240 self.failUnlessEqual(s.computeSourceRevision(b.allChanges()), 67)
3241
3242 class Patch(VCBase, unittest.TestCase):
3243 def setUp(self):
3244 pass
3245
3246 def tearDown(self):
3247 pass
3248
3249 def testPatch(self):
3250 # invoke 'patch' all by itself, to see if it works the way we think
3251 # it should. This is intended to ferret out some windows test
3252 # failures.
3253 helper = BaseHelper()
3254 self.workdir = os.path.join("test_vc", "testPatch")
3255 helper.populate(self.workdir)
3256 patch = which("patch")[0]
3257
3258 command = [patch, "-p0"]
3259 class FakeBuilder:
3260 usePTY = False
3261 def sendUpdate(self, status):
3262 pass
3263 c = commands.ShellCommand(FakeBuilder(), command, self.workdir,
3264 sendRC=False, initialStdin=p0_diff)
3265 d = c.start()
3266 d.addCallback(self._testPatch_1)
3267 return d
3268
3269 def _testPatch_1(self, res):
3270 # make sure the file actually got patched
3271 subdir_c = os.path.join(self.workdir, "subdir", "subdir.c")
3272 data = open(subdir_c, "r").read()
3273 self.failUnlessIn("HellÒ patched subdir.\\n", data)
OLDNEW
« no previous file with comments | « third_party/buildbot_7_12/buildbot/test/test_util.py ('k') | third_party/buildbot_7_12/buildbot/test/test_web.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698