# -*- test-case-name: buildbot.test.test_svnpoller -*- import time from twisted.internet import defer from twisted.trial import unittest from buildbot.changes.svnpoller import SVNPoller # this is the output of "svn info --xml # svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk" prefix_output = """\ svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk svn+ssh://svn.twistedmatrix.com/svn/Twisted bbbe8e31-12d6-0310-92fd-ac37d47ddeeb jml 2006-10-01T02:37:34.063255Z """ # and this is "svn info --xml svn://svn.twistedmatrix.com/svn/Twisted". I # think this is kind of a degenerate case.. it might even be a form of error. prefix_output_2 = """\ """ # this is the svn info output for a local repository, svn info --xml # file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository prefix_output_3 = """\ file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository c0f47ff4-ba1e-0410-96b5-d44cc5c79e7f warner 2006-10-01T07:37:04.182499Z """ # % svn info --xml file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk prefix_output_4 = """\ file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository c0f47ff4-ba1e-0410-96b5-d44cc5c79e7f warner 2006-10-01T07:37:02.286440Z """ class ComputePrefix(unittest.TestCase): def test1(self): base = "svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk" s = SVNPoller(base + "/") self.failUnlessEqual(s.svnurl, base) # certify slash-stripping prefix = s.determine_prefix(prefix_output) self.failUnlessEqual(prefix, "trunk") self.failUnlessEqual(s._prefix, prefix) def test2(self): base = "svn+ssh://svn.twistedmatrix.com/svn/Twisted" s = SVNPoller(base) self.failUnlessEqual(s.svnurl, base) prefix = s.determine_prefix(prefix_output_2) self.failUnlessEqual(prefix, "") def test3(self): base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository" s = SVNPoller(base) self.failUnlessEqual(s.svnurl, base) prefix = s.determine_prefix(prefix_output_3) self.failUnlessEqual(prefix, "") def test4(self): base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk" s = SVNPoller(base) self.failUnlessEqual(s.svnurl, base) prefix = s.determine_prefix(prefix_output_4) self.failUnlessEqual(prefix, "sample/trunk") # output from svn log on .../SVN-Repository/sample # (so it includes trunk and branches) sample_base = "file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test_vc/repositories/SVN-Repository/sample" sample_logentries = [None] * 6 sample_logentries[5] = """\ warner 2006-10-01T19:35:16.165664Z /sample/branch/version.c revised_to_2 """ sample_logentries[4] = """\ warner 2006-10-01T19:35:16.165664Z /sample/branch revised_to_2 """ sample_logentries[3] = """\ warner 2006-10-01T19:35:16.165664Z /sample/trunk/version.c revised_to_2 """ sample_logentries[2] = """\ warner 2006-10-01T19:35:10.215692Z /sample/branch/main.c commit_on_branch """ sample_logentries[1] = """\ warner 2006-10-01T19:35:09.154973Z /sample/branch make_branch """ sample_logentries[0] = """\ warner 2006-10-01T19:35:08.642045Z /sample /sample/trunk /sample/trunk/subdir/subdir.c /sample/trunk/main.c /sample/trunk/version.c /sample/trunk/subdir sample_project_files """ sample_info_output = """\ file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test_vc/repositories/SVN-Repository/sample file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test_vc/repositories/SVN-Repository 4f94adfc-c41e-0410-92d5-fbf86b7c7689 warner 2006-10-01T19:35:16.165664Z """ changes_output_template = """\ %s """ def make_changes_output(maxrevision): # return what 'svn log' would have just after the given revision was # committed logs = sample_logentries[0:maxrevision] assert len(logs) == maxrevision logs.reverse() output = changes_output_template % ("".join(logs)) return output def split_file(path): pieces = path.split("/") if pieces[0] == "branch": return "branch", "/".join(pieces[1:]) if pieces[0] == "trunk": return None, "/".join(pieces[1:]) raise RuntimeError("there shouldn't be any files like %s" % path) class MySVNPoller(SVNPoller): def __init__(self, *args, **kwargs): SVNPoller.__init__(self, *args, **kwargs) self.pending_commands = [] self.finished_changes = [] def getProcessOutput(self, args): d = defer.Deferred() self.pending_commands.append((args, d)) return d def submit_changes(self, changes): self.finished_changes.extend(changes) class ComputeChanges(unittest.TestCase): def test1(self): base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample" s = SVNPoller(base) s._prefix = "sample" output = make_changes_output(4) doc = s.parse_logs(output) newlast, logentries = s._filter_new_logentries(doc, 4) self.failUnlessEqual(newlast, 4) self.failUnlessEqual(len(logentries), 0) newlast, logentries = s._filter_new_logentries(doc, 3) self.failUnlessEqual(newlast, 4) self.failUnlessEqual(len(logentries), 1) newlast, logentries = s._filter_new_logentries(doc, 1) self.failUnlessEqual(newlast, 4) self.failUnlessEqual(len(logentries), 3) newlast, logentries = s._filter_new_logentries(doc, None) self.failUnlessEqual(newlast, 4) self.failUnlessEqual(len(logentries), 0) def testChanges(self): base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample" s = SVNPoller(base, split_file=split_file) s._prefix = "sample" doc = s.parse_logs(make_changes_output(3)) newlast, logentries = s._filter_new_logentries(doc, 1) # so we see revisions 2 and 3 as being new self.failUnlessEqual(newlast, 3) changes = s.create_changes(logentries) self.failUnlessEqual(len(changes), 2) self.failUnlessEqual(changes[0].branch, "branch") self.failUnlessEqual(changes[0].revision, '2') self.failUnlessEqual(changes[1].branch, "branch") self.failUnlessEqual(changes[1].files, ["main.c"]) self.failUnlessEqual(changes[1].revision, '3') # and now pull in r4 doc = s.parse_logs(make_changes_output(4)) newlast, logentries = s._filter_new_logentries(doc, newlast) self.failUnlessEqual(newlast, 4) # so we see revision 4 as being new changes = s.create_changes(logentries) self.failUnlessEqual(len(changes), 1) self.failUnlessEqual(changes[0].branch, None) self.failUnlessEqual(changes[0].revision, '4') self.failUnlessEqual(changes[0].files, ["version.c"]) # and now pull in r5 (should *not* create a change as it's a # branch deletion doc = s.parse_logs(make_changes_output(5)) newlast, logentries = s._filter_new_logentries(doc, newlast) self.failUnlessEqual(newlast, 5) # so we see revision 5 as being new changes = s.create_changes(logentries) self.failUnlessEqual(len(changes), 0) # and now pull in r6 (should create a change as it's not # deleting an entire branch doc = s.parse_logs(make_changes_output(6)) newlast, logentries = s._filter_new_logentries(doc, newlast) self.failUnlessEqual(newlast, 6) # so we see revision 6 as being new changes = s.create_changes(logentries) self.failUnlessEqual(len(changes), 1) self.failUnlessEqual(changes[0].branch, 'branch') self.failUnlessEqual(changes[0].revision, '6') self.failUnlessEqual(changes[0].files, ["version.c"]) def testFirstTime(self): base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample" s = SVNPoller(base, split_file=split_file) s._prefix = "sample" doc = s.parse_logs(make_changes_output(4)) logentries = s.get_new_logentries(doc) # SVNPoller ignores all changes that happened before it was started self.failUnlessEqual(len(logentries), 0) self.failUnlessEqual(s.last_change, 4) class Misc(unittest.TestCase): def testAlreadyWorking(self): base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample" s = MySVNPoller(base) d = s.checksvn() # the SVNPoller is now waiting for its getProcessOutput to finish self.failUnlessEqual(s.overrun_counter, 0) d2 = s.checksvn() self.failUnlessEqual(s.overrun_counter, 1) self.failUnlessEqual(len(s.pending_commands), 1) def testGetRoot(self): base = "svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk" s = MySVNPoller(base) d = s.checksvn() # the SVNPoller is now waiting for its getProcessOutput to finish self.failUnlessEqual(len(s.pending_commands), 1) self.failUnlessEqual(s.pending_commands[0][0], ["info", "--xml", "--non-interactive", base]) def makeTime(timestring): datefmt = '%Y/%m/%d %H:%M:%S' when = time.mktime(time.strptime(timestring, datefmt)) return when class Everything(unittest.TestCase): def test1(self): s = MySVNPoller(sample_base, split_file=split_file) d = s.checksvn() # the SVNPoller is now waiting for its getProcessOutput to finish self.failUnlessEqual(len(s.pending_commands), 1) self.failUnlessEqual(s.pending_commands[0][0], ["info", "--xml", "--non-interactive", sample_base]) d = s.pending_commands[0][1] s.pending_commands.pop(0) d.callback(sample_info_output) # now it should be waiting for the 'svn log' command self.failUnlessEqual(len(s.pending_commands), 1) self.failUnlessEqual(s.pending_commands[0][0], ["log", "--xml", "--verbose", "--non-interactive", "--limit=100", sample_base]) d = s.pending_commands[0][1] s.pending_commands.pop(0) d.callback(make_changes_output(1)) # the command ignores the first batch of changes self.failUnlessEqual(len(s.finished_changes), 0) self.failUnlessEqual(s.last_change, 1) # now fire it again, nothing changing d = s.checksvn() self.failUnlessEqual(s.pending_commands[0][0], ["log", "--xml", "--verbose", "--non-interactive", "--limit=100", sample_base]) d = s.pending_commands[0][1] s.pending_commands.pop(0) d.callback(make_changes_output(1)) # nothing has changed self.failUnlessEqual(len(s.finished_changes), 0) self.failUnlessEqual(s.last_change, 1) # and again, with r2 this time d = s.checksvn() self.failUnlessEqual(s.pending_commands[0][0], ["log", "--xml", "--verbose", "--non-interactive", "--limit=100", sample_base]) d = s.pending_commands[0][1] s.pending_commands.pop(0) d.callback(make_changes_output(2)) # r2 should appear self.failUnlessEqual(len(s.finished_changes), 1) self.failUnlessEqual(s.last_change, 2) c = s.finished_changes[0] self.failUnlessEqual(c.branch, "branch") self.failUnlessEqual(c.revision, '2') self.failUnlessEqual(c.files, ['']) # TODO: this is what creating the branch looks like: a Change with a # zero-length file. We should decide if we want filenames like this # in the Change (and make sure nobody else gets confused by it) or if # we want to strip them out. self.failUnlessEqual(c.comments, "make_branch") # and again at r2, so nothing should change d = s.checksvn() self.failUnlessEqual(s.pending_commands[0][0], ["log", "--xml", "--verbose", "--non-interactive", "--limit=100", sample_base]) d = s.pending_commands[0][1] s.pending_commands.pop(0) d.callback(make_changes_output(2)) # nothing has changed self.failUnlessEqual(len(s.finished_changes), 1) self.failUnlessEqual(s.last_change, 2) # and again with both r3 and r4 appearing together d = s.checksvn() self.failUnlessEqual(s.pending_commands[0][0], ["log", "--xml", "--verbose", "--non-interactive", "--limit=100", sample_base]) d = s.pending_commands[0][1] s.pending_commands.pop(0) d.callback(make_changes_output(4)) self.failUnlessEqual(len(s.finished_changes), 3) self.failUnlessEqual(s.last_change, 4) c3 = s.finished_changes[1] self.failUnlessEqual(c3.branch, "branch") self.failUnlessEqual(c3.revision, '3') self.failUnlessEqual(c3.files, ["main.c"]) self.failUnlessEqual(c3.comments, "commit_on_branch") c4 = s.finished_changes[2] self.failUnlessEqual(c4.branch, None) self.failUnlessEqual(c4.revision, '4') self.failUnlessEqual(c4.files, ["version.c"]) self.failUnlessEqual(c4.comments, "revised_to_2") self.failUnless(abs(c4.when - time.time()) < 60) # TODO: # get coverage of split_file returning None # point at a live SVN server for a little while