1 # -*- test-case-name: buildbot.test.test_svnpoller -*-
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"
17 <url>svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk</url>
19 <root>svn+ssh://svn.twistedmatrix.com/svn/Twisted</root>
20 <uuid>bbbe8e31-12d6-0310-92fd-ac37d47ddeeb</uuid>
25 <date>2006-10-01T02:37:34.063255Z</date>
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
= """\
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
= """\
48 <url>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository</url>
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>
55 <author>warner</author>
56 <date>2006-10-01T07:37:04.182499Z</date>
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
= """\
71 <url>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk</url>
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>
78 <author>warner</author>
79 <date>2006-10-01T07:37:02.286440Z</date>
87 class ComputePrefix(unittest
.TestCase
):
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
)
97 base
= "svn+ssh://svn.twistedmatrix.com/svn/Twisted"
99 self
.failUnlessEqual(s
.svnurl
, base
)
100 prefix
= s
.determine_prefix(prefix_output_2
)
101 self
.failUnlessEqual(prefix
, "")
104 base
= "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository"
106 self
.failUnlessEqual(s
.svnurl
, base
)
107 prefix
= s
.determine_prefix(prefix_output_3
)
108 self
.failUnlessEqual(prefix
, "")
111 base
= "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/sample/trunk"
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] = """\
125 <author>warner</author>
126 <date>2006-10-01T19:35:16.165664Z</date>
129 action="M">/sample/trunk/version.c</path>
131 <msg>revised_to_2</msg>
135 sample_logentries
[2] = """\
138 <author>warner</author>
139 <date>2006-10-01T19:35:10.215692Z</date>
142 action="M">/sample/branch/main.c</path>
144 <msg>commit_on_branch</msg>
148 sample_logentries
[1] = """\
151 <author>warner</author>
152 <date>2006-10-01T19:35:09.154973Z</date>
155 copyfrom-path="/sample/trunk"
157 action="A">/sample/branch</path>
159 <msg>make_branch</msg>
163 sample_logentries
[0] = """\
166 <author>warner</author>
167 <date>2006-10-01T19:35:08.642045Z</date>
170 action="A">/sample</path>
172 action="A">/sample/trunk</path>
174 action="A">/sample/trunk/subdir/subdir.c</path>
176 action="A">/sample/trunk/main.c</path>
178 action="A">/sample/trunk/version.c</path>
180 action="A">/sample/trunk/subdir</path>
182 <msg>sample_project_files</msg>
186 sample_info_output
= """\
187 <?xml version="1.0"?>
193 <url>file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test_vc/repositories/SVN-Repository/sample</url>
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>
200 <author>warner</author>
201 <date>2006-10-01T19:35:16.165664Z</date>
208 changes_output_template
= """\
209 <?xml version="1.0"?>
214 def make_changes_output(maxrevision
):
215 # return what 'svn log' would have just after the given revision was
217 logs
= sample_logentries
[0:maxrevision
]
218 assert len(logs
) == maxrevision
220 output
= changes_output_template
% ("".join(logs
))
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
):
239 self
.pending_commands
.append((args
, d
))
242 def submit_changes(self
, changes
):
243 self
.finished_changes
.extend(changes
)
245 class ComputeChanges(unittest
.TestCase
):
247 base
= "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/test_vc/repositories/SVN-Repository/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
)
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)
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
)
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
)
311 # the SVNPoller is now waiting for its getProcessOutput to finish
312 self
.failUnlessEqual(s
.overrun_counter
, 0)
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
)
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
))
332 class Everything(unittest
.TestCase
):
334 s
= MySVNPoller(sample_base
, split_file
=split_file
)
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",
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
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
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))
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
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
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)
428 # get coverage of split_file returning None
429 # point at a live SVN server for a little while