| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: buildbot.test.test_svnpoller -*- | |
| 2 | |
| 3 import time | |
| 4 from twisted.internet import defer | |
| 5 from twisted.trial import unittest | |
| 6 from buildbot.changes.svnpoller import SVNPoller | |
| 7 | |
| 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 """ | |
| 30 | |
| 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 """ | |
| 38 | |
| 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_v
c/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/tes
t_vc/repositories/SVN-Repository</url> | |
| 49 <repository> | |
| 50 <root>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/te
st_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 """ | |
| 61 | |
| 62 # % svn info --xml file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_
trial_temp/test_vc/repositories/SVN-Repository/sample/trunk | |
| 63 | |
| 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/tes
t_vc/repositories/SVN-Repository/sample/trunk</url> | |
| 72 <repository> | |
| 73 <root>file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_trial_temp/te
st_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 """ | |
| 84 | |
| 85 | |
| 86 | |
| 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) | |
| 95 | |
| 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, "") | |
| 102 | |
| 103 def test3(self): | |
| 104 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_tri
al_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, "") | |
| 109 | |
| 110 def test4(self): | |
| 111 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_tri
al_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") | |
| 116 | |
| 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] * 6 | |
| 121 | |
| 122 sample_logentries[5] = """\ | |
| 123 <logentry | |
| 124 revision="6"> | |
| 125 <author>warner</author> | |
| 126 <date>2006-10-01T19:35:16.165664Z</date> | |
| 127 <paths> | |
| 128 <path | |
| 129 action="D">/sample/branch/version.c</path> | |
| 130 </paths> | |
| 131 <msg>revised_to_2</msg> | |
| 132 </logentry> | |
| 133 """ | |
| 134 | |
| 135 sample_logentries[4] = """\ | |
| 136 <logentry | |
| 137 revision="5"> | |
| 138 <author>warner</author> | |
| 139 <date>2006-10-01T19:35:16.165664Z</date> | |
| 140 <paths> | |
| 141 <path | |
| 142 action="D">/sample/branch</path> | |
| 143 </paths> | |
| 144 <msg>revised_to_2</msg> | |
| 145 </logentry> | |
| 146 """ | |
| 147 | |
| 148 sample_logentries[3] = """\ | |
| 149 <logentry | |
| 150 revision="4"> | |
| 151 <author>warner</author> | |
| 152 <date>2006-10-01T19:35:16.165664Z</date> | |
| 153 <paths> | |
| 154 <path | |
| 155 action="M">/sample/trunk/version.c</path> | |
| 156 </paths> | |
| 157 <msg>revised_to_2</msg> | |
| 158 </logentry> | |
| 159 """ | |
| 160 | |
| 161 sample_logentries[2] = """\ | |
| 162 <logentry | |
| 163 revision="3"> | |
| 164 <author>warner</author> | |
| 165 <date>2006-10-01T19:35:10.215692Z</date> | |
| 166 <paths> | |
| 167 <path | |
| 168 action="M">/sample/branch/main.c</path> | |
| 169 </paths> | |
| 170 <msg>commit_on_branch</msg> | |
| 171 </logentry> | |
| 172 """ | |
| 173 | |
| 174 sample_logentries[1] = """\ | |
| 175 <logentry | |
| 176 revision="2"> | |
| 177 <author>warner</author> | |
| 178 <date>2006-10-01T19:35:09.154973Z</date> | |
| 179 <paths> | |
| 180 <path | |
| 181 copyfrom-path="/sample/trunk" | |
| 182 copyfrom-rev="1" | |
| 183 action="A">/sample/branch</path> | |
| 184 </paths> | |
| 185 <msg>make_branch</msg> | |
| 186 </logentry> | |
| 187 """ | |
| 188 | |
| 189 sample_logentries[0] = """\ | |
| 190 <logentry | |
| 191 revision="1"> | |
| 192 <author>warner</author> | |
| 193 <date>2006-10-01T19:35:08.642045Z</date> | |
| 194 <paths> | |
| 195 <path | |
| 196 action="A">/sample</path> | |
| 197 <path | |
| 198 action="A">/sample/trunk</path> | |
| 199 <path | |
| 200 action="A">/sample/trunk/subdir/subdir.c</path> | |
| 201 <path | |
| 202 action="A">/sample/trunk/main.c</path> | |
| 203 <path | |
| 204 action="A">/sample/trunk/version.c</path> | |
| 205 <path | |
| 206 action="A">/sample/trunk/subdir</path> | |
| 207 </paths> | |
| 208 <msg>sample_project_files</msg> | |
| 209 </logentry> | |
| 210 """ | |
| 211 | |
| 212 sample_info_output = """\ | |
| 213 <?xml version="1.0"?> | |
| 214 <info> | |
| 215 <entry | |
| 216 kind="dir" | |
| 217 path="sample" | |
| 218 revision="4"> | |
| 219 <url>file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/test
_vc/repositories/SVN-Repository/sample</url> | |
| 220 <repository> | |
| 221 <root>file:///usr/home/warner/stuff/Projects/BuildBot/trees/misc/_trial_temp/tes
t_vc/repositories/SVN-Repository</root> | |
| 222 <uuid>4f94adfc-c41e-0410-92d5-fbf86b7c7689</uuid> | |
| 223 </repository> | |
| 224 <commit | |
| 225 revision="4"> | |
| 226 <author>warner</author> | |
| 227 <date>2006-10-01T19:35:16.165664Z</date> | |
| 228 </commit> | |
| 229 </entry> | |
| 230 </info> | |
| 231 """ | |
| 232 | |
| 233 | |
| 234 changes_output_template = """\ | |
| 235 <?xml version="1.0"?> | |
| 236 <log> | |
| 237 %s</log> | |
| 238 """ | |
| 239 | |
| 240 def make_changes_output(maxrevision): | |
| 241 # return what 'svn log' would have just after the given revision was | |
| 242 # committed | |
| 243 logs = sample_logentries[0:maxrevision] | |
| 244 assert len(logs) == maxrevision | |
| 245 logs.reverse() | |
| 246 output = changes_output_template % ("".join(logs)) | |
| 247 return output | |
| 248 | |
| 249 def split_file(path): | |
| 250 pieces = path.split("/") | |
| 251 if pieces[0] == "branch": | |
| 252 return "branch", "/".join(pieces[1:]) | |
| 253 if pieces[0] == "trunk": | |
| 254 return None, "/".join(pieces[1:]) | |
| 255 raise RuntimeError("there shouldn't be any files like %s" % path) | |
| 256 | |
| 257 class MySVNPoller(SVNPoller): | |
| 258 def __init__(self, *args, **kwargs): | |
| 259 SVNPoller.__init__(self, *args, **kwargs) | |
| 260 self.pending_commands = [] | |
| 261 self.finished_changes = [] | |
| 262 | |
| 263 def getProcessOutput(self, args): | |
| 264 d = defer.Deferred() | |
| 265 self.pending_commands.append((args, d)) | |
| 266 return d | |
| 267 | |
| 268 def submit_changes(self, changes): | |
| 269 self.finished_changes.extend(changes) | |
| 270 | |
| 271 class ComputeChanges(unittest.TestCase): | |
| 272 def test1(self): | |
| 273 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_tri
al_temp/test_vc/repositories/SVN-Repository/sample" | |
| 274 s = SVNPoller(base) | |
| 275 s._prefix = "sample" | |
| 276 output = make_changes_output(4) | |
| 277 doc = s.parse_logs(output) | |
| 278 | |
| 279 newlast, logentries = s._filter_new_logentries(doc, 4) | |
| 280 self.failUnlessEqual(newlast, 4) | |
| 281 self.failUnlessEqual(len(logentries), 0) | |
| 282 | |
| 283 newlast, logentries = s._filter_new_logentries(doc, 3) | |
| 284 self.failUnlessEqual(newlast, 4) | |
| 285 self.failUnlessEqual(len(logentries), 1) | |
| 286 | |
| 287 newlast, logentries = s._filter_new_logentries(doc, 1) | |
| 288 self.failUnlessEqual(newlast, 4) | |
| 289 self.failUnlessEqual(len(logentries), 3) | |
| 290 | |
| 291 newlast, logentries = s._filter_new_logentries(doc, None) | |
| 292 self.failUnlessEqual(newlast, 4) | |
| 293 self.failUnlessEqual(len(logentries), 0) | |
| 294 | |
| 295 def testChanges(self): | |
| 296 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_tri
al_temp/test_vc/repositories/SVN-Repository/sample" | |
| 297 s = SVNPoller(base, split_file=split_file) | |
| 298 s._prefix = "sample" | |
| 299 doc = s.parse_logs(make_changes_output(3)) | |
| 300 newlast, logentries = s._filter_new_logentries(doc, 1) | |
| 301 # so we see revisions 2 and 3 as being new | |
| 302 self.failUnlessEqual(newlast, 3) | |
| 303 changes = s.create_changes(logentries) | |
| 304 self.failUnlessEqual(len(changes), 2) | |
| 305 self.failUnlessEqual(changes[0].branch, "branch") | |
| 306 self.failUnlessEqual(changes[0].revision, '2') | |
| 307 self.failUnlessEqual(changes[1].branch, "branch") | |
| 308 self.failUnlessEqual(changes[1].files, ["main.c"]) | |
| 309 self.failUnlessEqual(changes[1].revision, '3') | |
| 310 | |
| 311 # and now pull in r4 | |
| 312 doc = s.parse_logs(make_changes_output(4)) | |
| 313 newlast, logentries = s._filter_new_logentries(doc, newlast) | |
| 314 self.failUnlessEqual(newlast, 4) | |
| 315 # so we see revision 4 as being new | |
| 316 changes = s.create_changes(logentries) | |
| 317 self.failUnlessEqual(len(changes), 1) | |
| 318 self.failUnlessEqual(changes[0].branch, None) | |
| 319 self.failUnlessEqual(changes[0].revision, '4') | |
| 320 self.failUnlessEqual(changes[0].files, ["version.c"]) | |
| 321 | |
| 322 # and now pull in r5 (should *not* create a change as it's a | |
| 323 # branch deletion | |
| 324 doc = s.parse_logs(make_changes_output(5)) | |
| 325 newlast, logentries = s._filter_new_logentries(doc, newlast) | |
| 326 self.failUnlessEqual(newlast, 5) | |
| 327 # so we see revision 5 as being new | |
| 328 changes = s.create_changes(logentries) | |
| 329 self.failUnlessEqual(len(changes), 0) | |
| 330 | |
| 331 # and now pull in r6 (should create a change as it's not | |
| 332 # deleting an entire branch | |
| 333 doc = s.parse_logs(make_changes_output(6)) | |
| 334 newlast, logentries = s._filter_new_logentries(doc, newlast) | |
| 335 self.failUnlessEqual(newlast, 6) | |
| 336 # so we see revision 6 as being new | |
| 337 changes = s.create_changes(logentries) | |
| 338 self.failUnlessEqual(len(changes), 1) | |
| 339 self.failUnlessEqual(changes[0].branch, 'branch') | |
| 340 self.failUnlessEqual(changes[0].revision, '6') | |
| 341 self.failUnlessEqual(changes[0].files, ["version.c"]) | |
| 342 | |
| 343 def testFirstTime(self): | |
| 344 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_tri
al_temp/test_vc/repositories/SVN-Repository/sample" | |
| 345 s = SVNPoller(base, split_file=split_file) | |
| 346 s._prefix = "sample" | |
| 347 doc = s.parse_logs(make_changes_output(4)) | |
| 348 logentries = s.get_new_logentries(doc) | |
| 349 # SVNPoller ignores all changes that happened before it was started | |
| 350 self.failUnlessEqual(len(logentries), 0) | |
| 351 self.failUnlessEqual(s.last_change, 4) | |
| 352 | |
| 353 class Misc(unittest.TestCase): | |
| 354 def testAlreadyWorking(self): | |
| 355 base = "file:///home/warner/stuff/Projects/BuildBot/trees/svnpoller/_tri
al_temp/test_vc/repositories/SVN-Repository/sample" | |
| 356 s = MySVNPoller(base) | |
| 357 d = s.checksvn() | |
| 358 # the SVNPoller is now waiting for its getProcessOutput to finish | |
| 359 self.failUnlessEqual(s.overrun_counter, 0) | |
| 360 d2 = s.checksvn() | |
| 361 self.failUnlessEqual(s.overrun_counter, 1) | |
| 362 self.failUnlessEqual(len(s.pending_commands), 1) | |
| 363 | |
| 364 def testGetRoot(self): | |
| 365 base = "svn+ssh://svn.twistedmatrix.com/svn/Twisted/trunk" | |
| 366 s = MySVNPoller(base) | |
| 367 d = s.checksvn() | |
| 368 # the SVNPoller is now waiting for its getProcessOutput to finish | |
| 369 self.failUnlessEqual(len(s.pending_commands), 1) | |
| 370 self.failUnlessEqual(s.pending_commands[0][0], | |
| 371 ["info", "--xml", "--non-interactive", base]) | |
| 372 | |
| 373 def makeTime(timestring): | |
| 374 datefmt = '%Y/%m/%d %H:%M:%S' | |
| 375 when = time.mktime(time.strptime(timestring, datefmt)) | |
| 376 return when | |
| 377 | |
| 378 | |
| 379 class Everything(unittest.TestCase): | |
| 380 def test1(self): | |
| 381 s = MySVNPoller(sample_base, split_file=split_file) | |
| 382 d = s.checksvn() | |
| 383 # the SVNPoller is now waiting for its getProcessOutput to finish | |
| 384 self.failUnlessEqual(len(s.pending_commands), 1) | |
| 385 self.failUnlessEqual(s.pending_commands[0][0], | |
| 386 ["info", "--xml", "--non-interactive", | |
| 387 sample_base]) | |
| 388 d = s.pending_commands[0][1] | |
| 389 s.pending_commands.pop(0) | |
| 390 d.callback(sample_info_output) | |
| 391 # now it should be waiting for the 'svn log' command | |
| 392 self.failUnlessEqual(len(s.pending_commands), 1) | |
| 393 self.failUnlessEqual(s.pending_commands[0][0], | |
| 394 ["log", "--xml", "--verbose", "--non-interactive", | |
| 395 "--limit=100", sample_base]) | |
| 396 d = s.pending_commands[0][1] | |
| 397 s.pending_commands.pop(0) | |
| 398 d.callback(make_changes_output(1)) | |
| 399 # the command ignores the first batch of changes | |
| 400 self.failUnlessEqual(len(s.finished_changes), 0) | |
| 401 self.failUnlessEqual(s.last_change, 1) | |
| 402 | |
| 403 # now fire it again, nothing changing | |
| 404 d = s.checksvn() | |
| 405 self.failUnlessEqual(s.pending_commands[0][0], | |
| 406 ["log", "--xml", "--verbose", "--non-interactive", | |
| 407 "--limit=100", sample_base]) | |
| 408 d = s.pending_commands[0][1] | |
| 409 s.pending_commands.pop(0) | |
| 410 d.callback(make_changes_output(1)) | |
| 411 # nothing has changed | |
| 412 self.failUnlessEqual(len(s.finished_changes), 0) | |
| 413 self.failUnlessEqual(s.last_change, 1) | |
| 414 | |
| 415 # and again, with r2 this time | |
| 416 d = s.checksvn() | |
| 417 self.failUnlessEqual(s.pending_commands[0][0], | |
| 418 ["log", "--xml", "--verbose", "--non-interactive", | |
| 419 "--limit=100", sample_base]) | |
| 420 d = s.pending_commands[0][1] | |
| 421 s.pending_commands.pop(0) | |
| 422 d.callback(make_changes_output(2)) | |
| 423 # r2 should appear | |
| 424 self.failUnlessEqual(len(s.finished_changes), 1) | |
| 425 self.failUnlessEqual(s.last_change, 2) | |
| 426 | |
| 427 c = s.finished_changes[0] | |
| 428 self.failUnlessEqual(c.branch, "branch") | |
| 429 self.failUnlessEqual(c.revision, '2') | |
| 430 self.failUnlessEqual(c.files, ['']) | |
| 431 # TODO: this is what creating the branch looks like: a Change with a | |
| 432 # zero-length file. We should decide if we want filenames like this | |
| 433 # in the Change (and make sure nobody else gets confused by it) or if | |
| 434 # we want to strip them out. | |
| 435 self.failUnlessEqual(c.comments, "make_branch") | |
| 436 | |
| 437 # and again at r2, so nothing should change | |
| 438 d = s.checksvn() | |
| 439 self.failUnlessEqual(s.pending_commands[0][0], | |
| 440 ["log", "--xml", "--verbose", "--non-interactive", | |
| 441 "--limit=100", sample_base]) | |
| 442 d = s.pending_commands[0][1] | |
| 443 s.pending_commands.pop(0) | |
| 444 d.callback(make_changes_output(2)) | |
| 445 # nothing has changed | |
| 446 self.failUnlessEqual(len(s.finished_changes), 1) | |
| 447 self.failUnlessEqual(s.last_change, 2) | |
| 448 | |
| 449 # and again with both r3 and r4 appearing together | |
| 450 d = s.checksvn() | |
| 451 self.failUnlessEqual(s.pending_commands[0][0], | |
| 452 ["log", "--xml", "--verbose", "--non-interactive", | |
| 453 "--limit=100", sample_base]) | |
| 454 d = s.pending_commands[0][1] | |
| 455 s.pending_commands.pop(0) | |
| 456 d.callback(make_changes_output(4)) | |
| 457 self.failUnlessEqual(len(s.finished_changes), 3) | |
| 458 self.failUnlessEqual(s.last_change, 4) | |
| 459 | |
| 460 c3 = s.finished_changes[1] | |
| 461 self.failUnlessEqual(c3.branch, "branch") | |
| 462 self.failUnlessEqual(c3.revision, '3') | |
| 463 self.failUnlessEqual(c3.files, ["main.c"]) | |
| 464 self.failUnlessEqual(c3.comments, "commit_on_branch") | |
| 465 | |
| 466 c4 = s.finished_changes[2] | |
| 467 self.failUnlessEqual(c4.branch, None) | |
| 468 self.failUnlessEqual(c4.revision, '4') | |
| 469 self.failUnlessEqual(c4.files, ["version.c"]) | |
| 470 self.failUnlessEqual(c4.comments, "revised_to_2") | |
| 471 self.failUnless(abs(c4.when - time.time()) < 60) | |
| 472 | |
| 473 | |
| 474 # TODO: | |
| 475 # get coverage of split_file returning None | |
| 476 # point at a live SVN server for a little while | |
| OLD | NEW |