more NEWS items
[buildbot.git] / buildbot / test / test_svnpoller.py
blobbcc4141f497cffe57e0be216eda35c8873324eb2
1 # -*- test-case-name: buildbot.test.test_svnpoller -*-
3 import time
4 from twisted.internet import defer
5 from twisted.trial import unittest
6 from buildbot.changes.svnpoller import SVNPoller
8 # this is the output of "svn info --xml
9 # svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk"
10 prefix_output = """\
11 <?xml version="1.0"?>
12 <info>
13 <entry
14 kind="dir"
15 path="trunk"
16 revision="18354">
17 <url>svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk</url>
18 <repository>
19 <root>svn+ssh://svn.twistedmatrix.com/svn/Twisted</root>
20 <uuid>bbbe8e31-12d6-0310-92fd-ac37d47ddeeb</uuid>
21 </repository>
22 <commit
23 revision="18352">
24 <author>jml</author>
25 <date>2006-10-01T02:37:34.063255Z</date>
26 </commit>
27 </entry>
28 </info>
29 """
31 # and this is "svn info --xml svn://svn.twistedmatrix.com/svn/Twisted". I
32 # think this is kind of a degenerate case.. it might even be a form of error.
33 prefix_output_2 = """\
34 <?xml version="1.0"?>
35 <info>
36 </info>
37 """
39 # this is the svn info output for a local repository, svn info --xml
40 # file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository
41 prefix_output_3 = """\
42 <?xml version="1.0"?>
43 <info>
44 <entry
45 kind="dir"
46 path="SVN-Repository"
47 revision="3">
48 <url>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository</url>
49 <repository>
50 <root>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository</root>
51 <uuid>c0f47ff4-ba1e-0410-96b5-d44cc5c79e7f</uuid>
52 </repository>
53 <commit
54 revision="3">
55 <author>warner</author>
56 <date>2006-10-01T07:37:04.182499Z</date>
57 </commit>
58 </entry>
59 </info>
60 """
62 # % svn info --xml file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk
64 prefix_output_4 = """\
65 <?xml version="1.0"?>
66 <info>
67 <entry
68 kind="dir"
69 path="trunk"
70 revision="3">
71 <url>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk</url>
72 <repository>
73 <root>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository</root>
74 <uuid>c0f47ff4-ba1e-0410-96b5-d44cc5c79e7f</uuid>
75 </repository>
76 <commit
77 revision="1">
78 <author>warner</author>
79 <date>2006-10-01T07:37:02.286440Z</date>
80 </commit>
81 </entry>
82 </info>
83 """
87 class ComputePrefix(unittest.TestCase):
88 def test1(self):
89 base = "svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk"
90 s = SVNPoller(base + "/")
91 self.failUnlessEqual(s.svnurl, base) # certify slash-stripping
92 prefix = s.determine_prefix(prefix_output)
93 self.failUnlessEqual(prefix, "trunk")
94 self.failUnlessEqual(s._prefix, prefix)
96 def test2(self):
97 base = "svn+ssh://svn.twistedmatrix.com/svn/Twisted"
98 s = SVNPoller(base)
99 self.failUnlessEqual(s.svnurl, base)
100 prefix = s.determine_prefix(prefix_output_2)
101 self.failUnlessEqual(prefix, "")
103 def test3(self):
104 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository"
105 s = SVNPoller(base)
106 self.failUnlessEqual(s.svnurl, base)
107 prefix = s.determine_prefix(prefix_output_3)
108 self.failUnlessEqual(prefix, "")
110 def test4(self):
111 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk"
112 s = SVNPoller(base)
113 self.failUnlessEqual(s.svnurl, base)
114 prefix = s.determine_prefix(prefix_output_4)
115 self.failUnlessEqual(prefix, "sample/trunk")
117 # output from svn log on .../SVN-Repository/sample
118 # (so it includes trunk and branches)
119 sample_base = "file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test_vc/repositories/SVN-Repository/sample"
120 sample_logentries = [None] * 4
122 sample_logentries[3] = """\
123 <logentry
124 revision="4">
125 <author>warner</author>
126 <date>2006-10-01T19:35:16.165664Z</date>
127 <paths>
128 <path
129 action="M">/sample/trunk/version.c</path>
130 </paths>
131 <msg>revised_to_2</msg>
132 </logentry>
135 sample_logentries[2] = """\
136 <logentry
137 revision="3">
138 <author>warner</author>
139 <date>2006-10-01T19:35:10.215692Z</date>
140 <paths>
141 <path
142 action="M">/sample/branch/main.c</path>
143 </paths>
144 <msg>commit_on_branch</msg>
145 </logentry>
148 sample_logentries[1] = """\
149 <logentry
150 revision="2">
151 <author>warner</author>
152 <date>2006-10-01T19:35:09.154973Z</date>
153 <paths>
154 <path
155 copyfrom-path="/sample/trunk"
156 copyfrom-rev="1"
157 action="A">/sample/branch</path>
158 </paths>
159 <msg>make_branch</msg>
160 </logentry>
163 sample_logentries[0] = """\
164 <logentry
165 revision="1">
166 <author>warner</author>
167 <date>2006-10-01T19:35:08.642045Z</date>
168 <paths>
169 <path
170 action="A">/sample</path>
171 <path
172 action="A">/sample/trunk</path>
173 <path
174 action="A">/sample/trunk/subdir/subdir.c</path>
175 <path
176 action="A">/sample/trunk/main.c</path>
177 <path
178 action="A">/sample/trunk/version.c</path>
179 <path
180 action="A">/sample/trunk/subdir</path>
181 </paths>
182 <msg>sample_project_files</msg>
183 </logentry>
186 sample_info_output = """\
187 <?xml version="1.0"?>
188 <info>
189 <entry
190 kind="dir"
191 path="sample"
192 revision="4">
193 <url>file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test_vc/repositories/SVN-Repository/sample</url>
194 <repository>
195 <root>file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test_vc/repositories/SVN-Repository</root>
196 <uuid>4f94adfc-c41e-0410-92d5-fbf86b7c7689</uuid>
197 </repository>
198 <commit
199 revision="4">
200 <author>warner</author>
201 <date>2006-10-01T19:35:16.165664Z</date>
202 </commit>
203 </entry>
204 </info>
208 changes_output_template = """\
209 <?xml version="1.0"?>
210 <log>
211 %s</log>
214 def make_changes_output(maxrevision):
215 # return what 'svn log' would have just after the given revision was
216 # committed
217 logs = sample_logentries[0:maxrevision]
218 assert len(logs) == maxrevision
219 logs.reverse()
220 output = changes_output_template % ("".join(logs))
221 return output
223 def split_file(path):
224 pieces = path.split("/")
225 if pieces[0] == "branch":
226 return "branch", "/".join(pieces[1:])
227 if pieces[0] == "trunk":
228 return None, "/".join(pieces[1:])
229 raise RuntimeError("there shouldn't be any files like %s" % path)
231 class MySVNPoller(SVNPoller):
232 def __init__(self, *args, **kwargs):
233 SVNPoller.__init__(self, *args, **kwargs)
234 self.pending_commands = []
235 self.finished_changes = []
237 def getProcessOutput(self, args):
238 d = defer.Deferred()
239 self.pending_commands.append((args, d))
240 return d
242 def submit_changes(self, changes):
243 self.finished_changes.extend(changes)
245 class ComputeChanges(unittest.TestCase):
246 def test1(self):
247 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample"
248 s = SVNPoller(base)
249 s._prefix = "sample"
250 output = make_changes_output(4)
251 doc = s.parse_logs(output)
253 newlast, logentries = s._filter_new_logentries(doc, 4)
254 self.failUnlessEqual(newlast, 4)
255 self.failUnlessEqual(len(logentries), 0)
257 newlast, logentries = s._filter_new_logentries(doc, 3)
258 self.failUnlessEqual(newlast, 4)
259 self.failUnlessEqual(len(logentries), 1)
261 newlast, logentries = s._filter_new_logentries(doc, 1)
262 self.failUnlessEqual(newlast, 4)
263 self.failUnlessEqual(len(logentries), 3)
265 newlast, logentries = s._filter_new_logentries(doc, None)
266 self.failUnlessEqual(newlast, 4)
267 self.failUnlessEqual(len(logentries), 0)
269 def testChanges(self):
270 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample"
271 s = SVNPoller(base, split_file=split_file)
272 s._prefix = "sample"
273 doc = s.parse_logs(make_changes_output(3))
274 newlast, logentries = s._filter_new_logentries(doc, 1)
275 # so we see revisions 2 and 3 as being new
276 self.failUnlessEqual(newlast, 3)
277 changes = s.create_changes(logentries)
278 self.failUnlessEqual(len(changes), 2)
279 self.failUnlessEqual(changes[0].branch, "branch")
280 self.failUnlessEqual(changes[0].revision, 2)
281 self.failUnlessEqual(changes[1].branch, "branch")
282 self.failUnlessEqual(changes[1].files, ["main.c"])
283 self.failUnlessEqual(changes[1].revision, 3)
285 # and now pull in r4
286 doc = s.parse_logs(make_changes_output(4))
287 newlast, logentries = s._filter_new_logentries(doc, newlast)
288 self.failUnlessEqual(newlast, 4)
289 # so we see revision 4 as being new
290 changes = s.create_changes(logentries)
291 self.failUnlessEqual(len(changes), 1)
292 self.failUnlessEqual(changes[0].branch, None)
293 self.failUnlessEqual(changes[0].revision, 4)
294 self.failUnlessEqual(changes[0].files, ["version.c"])
296 def testFirstTime(self):
297 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample"
298 s = SVNPoller(base, split_file=split_file)
299 s._prefix = "sample"
300 doc = s.parse_logs(make_changes_output(4))
301 logentries = s.get_new_logentries(doc)
302 # SVNPoller ignores all changes that happened before it was started
303 self.failUnlessEqual(len(logentries), 0)
304 self.failUnlessEqual(s.last_change, 4)
306 class Misc(unittest.TestCase):
307 def testAlreadyWorking(self):
308 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample"
309 s = MySVNPoller(base)
310 d = s.checksvn()
311 # the SVNPoller is now waiting for its getProcessOutput to finish
312 self.failUnlessEqual(s.overrun_counter, 0)
313 d2 = s.checksvn()
314 self.failUnlessEqual(s.overrun_counter, 1)
315 self.failUnlessEqual(len(s.pending_commands), 1)
317 def testGetRoot(self):
318 base = "svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk"
319 s = MySVNPoller(base)
320 d = s.checksvn()
321 # the SVNPoller is now waiting for its getProcessOutput to finish
322 self.failUnlessEqual(len(s.pending_commands), 1)
323 self.failUnlessEqual(s.pending_commands[0][0],
324 ["info", "--xml", "--non-interactive", base])
326 def makeTime(timestring):
327 datefmt = '%Y/%m/%d %H:%M:%S'
328 when = time.mktime(time.strptime(timestring, datefmt))
329 return when
332 class Everything(unittest.TestCase):
333 def test1(self):
334 s = MySVNPoller(sample_base, split_file=split_file)
335 d = s.checksvn()
336 # the SVNPoller is now waiting for its getProcessOutput to finish
337 self.failUnlessEqual(len(s.pending_commands), 1)
338 self.failUnlessEqual(s.pending_commands[0][0],
339 ["info", "--xml", "--non-interactive",
340 sample_base])
341 d = s.pending_commands[0][1]
342 s.pending_commands.pop(0)
343 d.callback(sample_info_output)
344 # now it should be waiting for the 'svn log' command
345 self.failUnlessEqual(len(s.pending_commands), 1)
346 self.failUnlessEqual(s.pending_commands[0][0],
347 ["log", "--xml", "--verbose", "--non-interactive",
348 "--limit=100", sample_base])
349 d = s.pending_commands[0][1]
350 s.pending_commands.pop(0)
351 d.callback(make_changes_output(1))
352 # the command ignores the first batch of changes
353 self.failUnlessEqual(len(s.finished_changes), 0)
354 self.failUnlessEqual(s.last_change, 1)
356 # now fire it again, nothing changing
357 d = s.checksvn()
358 self.failUnlessEqual(s.pending_commands[0][0],
359 ["log", "--xml", "--verbose", "--non-interactive",
360 "--limit=100", sample_base])
361 d = s.pending_commands[0][1]
362 s.pending_commands.pop(0)
363 d.callback(make_changes_output(1))
364 # nothing has changed
365 self.failUnlessEqual(len(s.finished_changes), 0)
366 self.failUnlessEqual(s.last_change, 1)
368 # and again, with r2 this time
369 d = s.checksvn()
370 self.failUnlessEqual(s.pending_commands[0][0],
371 ["log", "--xml", "--verbose", "--non-interactive",
372 "--limit=100", sample_base])
373 d = s.pending_commands[0][1]
374 s.pending_commands.pop(0)
375 d.callback(make_changes_output(2))
376 # r2 should appear
377 self.failUnlessEqual(len(s.finished_changes), 1)
378 self.failUnlessEqual(s.last_change, 2)
380 c = s.finished_changes[0]
381 self.failUnlessEqual(c.branch, "branch")
382 self.failUnlessEqual(c.revision, 2)
383 self.failUnlessEqual(c.files, [''])
384 # TODO: this is what creating the branch looks like: a Change with a
385 # zero-length file. We should decide if we want filenames like this
386 # in the Change (and make sure nobody else gets confused by it) or if
387 # we want to strip them out.
388 self.failUnlessEqual(c.comments, "make_branch")
390 # and again at r2, so nothing should change
391 d = s.checksvn()
392 self.failUnlessEqual(s.pending_commands[0][0],
393 ["log", "--xml", "--verbose", "--non-interactive",
394 "--limit=100", sample_base])
395 d = s.pending_commands[0][1]
396 s.pending_commands.pop(0)
397 d.callback(make_changes_output(2))
398 # nothing has changed
399 self.failUnlessEqual(len(s.finished_changes), 1)
400 self.failUnlessEqual(s.last_change, 2)
402 # and again with both r3 and r4 appearing together
403 d = s.checksvn()
404 self.failUnlessEqual(s.pending_commands[0][0],
405 ["log", "--xml", "--verbose", "--non-interactive",
406 "--limit=100", sample_base])
407 d = s.pending_commands[0][1]
408 s.pending_commands.pop(0)
409 d.callback(make_changes_output(4))
410 self.failUnlessEqual(len(s.finished_changes), 3)
411 self.failUnlessEqual(s.last_change, 4)
413 c3 = s.finished_changes[1]
414 self.failUnlessEqual(c3.branch, "branch")
415 self.failUnlessEqual(c3.revision, 3)
416 self.failUnlessEqual(c3.files, ["main.c"])
417 self.failUnlessEqual(c3.comments, "commit_on_branch")
419 c4 = s.finished_changes[2]
420 self.failUnlessEqual(c4.branch, None)
421 self.failUnlessEqual(c4.revision, 4)
422 self.failUnlessEqual(c4.files, ["version.c"])
423 self.failUnlessEqual(c4.comments, "revised_to_2")
424 self.failUnless(abs(c4.when - time.time()) < 60)
427 # TODO:
428 # get coverage of split_file returning None
429 # point at a live SVN server for a little while