1import py 2import os, sys 3import pytest 4from py._path.svnwc import InfoSvnWCCommand, XMLWCStatus, parse_wcinfotime 5from py._path import svnwc as svncommon 6from svntestbase import CommonSvnTests 7 8 9pytestmark = pytest.mark.xfail(sys.platform.startswith('win'), 10 reason='#161 all tests in this file are failing on Windows', 11 run=False) 12 13 14def test_make_repo(path1, tmpdir): 15 repo = tmpdir.join("repo") 16 py.process.cmdexec('svnadmin create %s' % repo) 17 if sys.platform == 'win32': 18 repo = '/' + str(repo).replace('\\', '/') 19 repo = py.path.svnurl("file://%s" % repo) 20 wc = py.path.svnwc(tmpdir.join("wc")) 21 wc.checkout(repo) 22 assert wc.rev == 0 23 assert len(wc.listdir()) == 0 24 p = wc.join("a_file") 25 p.write("test file") 26 p.add() 27 rev = wc.commit("some test") 28 assert p.info().rev == 1 29 assert rev == 1 30 rev = wc.commit() 31 assert rev is None 32 33def pytest_funcarg__path1(request): 34 repo, repourl, wc = request.getfuncargvalue("repowc1") 35 return wc 36 37class TestWCSvnCommandPath(CommonSvnTests): 38 def test_status_attributes_simple(self, path1): 39 def assert_nochange(p): 40 s = p.status() 41 assert not s.modified 42 assert not s.prop_modified 43 assert not s.added 44 assert not s.deleted 45 assert not s.replaced 46 47 dpath = path1.join('sampledir') 48 assert_nochange(path1.join('sampledir')) 49 assert_nochange(path1.join('samplefile')) 50 51 def test_status_added(self, path1): 52 nf = path1.join('newfile') 53 nf.write('hello') 54 nf.add() 55 try: 56 s = nf.status() 57 assert s.added 58 assert not s.modified 59 assert not s.prop_modified 60 assert not s.replaced 61 finally: 62 nf.revert() 63 64 def test_status_change(self, path1): 65 nf = path1.join('samplefile') 66 try: 67 nf.write(nf.read() + 'change') 68 s = nf.status() 69 assert not s.added 70 assert s.modified 71 assert not s.prop_modified 72 assert not s.replaced 73 finally: 74 nf.revert() 75 76 def test_status_added_ondirectory(self, path1): 77 sampledir = path1.join('sampledir') 78 try: 79 t2 = sampledir.mkdir('t2') 80 t1 = t2.join('t1') 81 t1.write('test') 82 t1.add() 83 s = sampledir.status(rec=1) 84 # Comparing just the file names, because paths are unpredictable 85 # on Windows. (long vs. 8.3 paths) 86 assert t1.basename in [item.basename for item in s.added] 87 assert t2.basename in [item.basename for item in s.added] 88 finally: 89 t2.revert(rec=1) 90 t2.localpath.remove(rec=1) 91 92 def test_status_unknown(self, path1): 93 t1 = path1.join('un1') 94 try: 95 t1.write('test') 96 s = path1.status() 97 # Comparing just the file names, because paths are unpredictable 98 # on Windows. (long vs. 8.3 paths) 99 assert t1.basename in [item.basename for item in s.unknown] 100 finally: 101 t1.localpath.remove() 102 103 def test_status_unchanged(self, path1): 104 r = path1 105 s = path1.status(rec=1) 106 # Comparing just the file names, because paths are unpredictable 107 # on Windows. (long vs. 8.3 paths) 108 assert r.join('samplefile').basename in [item.basename 109 for item in s.unchanged] 110 assert r.join('sampledir').basename in [item.basename 111 for item in s.unchanged] 112 assert r.join('sampledir/otherfile').basename in [item.basename 113 for item in s.unchanged] 114 115 def test_status_update(self, path1): 116 # not a mark because the global "pytestmark" will end up overwriting a mark here 117 pytest.xfail("svn-1.7 has buggy 'status --xml' output") 118 r = path1 119 try: 120 r.update(rev=1) 121 s = r.status(updates=1, rec=1) 122 # Comparing just the file names, because paths are unpredictable 123 # on Windows. (long vs. 8.3 paths) 124 import pprint 125 pprint.pprint(s.allpath()) 126 assert r.join('anotherfile').basename in [item.basename for 127 item in s.update_available] 128 #assert len(s.update_available) == 1 129 finally: 130 r.update() 131 132 def test_status_replaced(self, path1): 133 p = path1.join("samplefile") 134 p.remove() 135 p.ensure(dir=0) 136 try: 137 s = path1.status() 138 assert p.basename in [item.basename for item in s.replaced] 139 finally: 140 path1.revert(rec=1) 141 142 def test_status_ignored(self, path1): 143 try: 144 d = path1.join('sampledir') 145 p = py.path.local(d).join('ignoredfile') 146 p.ensure(file=True) 147 s = d.status() 148 assert [x.basename for x in s.unknown] == ['ignoredfile'] 149 assert [x.basename for x in s.ignored] == [] 150 d.propset('svn:ignore', 'ignoredfile') 151 s = d.status() 152 assert [x.basename for x in s.unknown] == [] 153 assert [x.basename for x in s.ignored] == ['ignoredfile'] 154 finally: 155 path1.revert(rec=1) 156 157 def test_status_conflict(self, path1, tmpdir): 158 wc = path1 159 wccopy = py.path.svnwc(tmpdir.join("conflict_copy")) 160 wccopy.checkout(wc.url) 161 p = wc.ensure('conflictsamplefile', file=1) 162 p.write('foo') 163 wc.commit('added conflictsamplefile') 164 wccopy.update() 165 assert wccopy.join('conflictsamplefile').check() 166 p.write('bar') 167 wc.commit('wrote some data') 168 wccopy.join('conflictsamplefile').write('baz') 169 wccopy.update(interactive=False) 170 s = wccopy.status() 171 assert [x.basename for x in s.conflict] == ['conflictsamplefile'] 172 173 def test_status_external(self, path1, repowc2): 174 otherrepo, otherrepourl, otherwc = repowc2 175 d = path1.ensure('sampledir', dir=1) 176 try: 177 d.update() 178 d.propset('svn:externals', 'otherwc %s' % (otherwc.url,)) 179 d.update() 180 s = d.status() 181 assert [x.basename for x in s.external] == ['otherwc'] 182 assert 'otherwc' not in [x.basename for x in s.unchanged] 183 s = d.status(rec=1) 184 assert [x.basename for x in s.external] == ['otherwc'] 185 assert 'otherwc' in [x.basename for x in s.unchanged] 186 finally: 187 path1.revert(rec=1) 188 189 def test_status_deleted(self, path1): 190 d = path1.ensure('sampledir', dir=1) 191 d.remove() 192 d.ensure(dir=1) 193 path1.commit() 194 d.ensure('deletefile', dir=0) 195 d.commit() 196 s = d.status() 197 assert 'deletefile' in [x.basename for x in s.unchanged] 198 assert not s.deleted 199 p = d.join('deletefile') 200 p.remove() 201 s = d.status() 202 assert 'deletefile' not in s.unchanged 203 assert [x.basename for x in s.deleted] == ['deletefile'] 204 205 def test_status_noauthor(self, path1): 206 # testing for XML without author - this used to raise an exception 207 xml = '''\ 208 <entry path="/tmp/pytest-23/wc"> 209 <wc-status item="normal" props="none" revision="0"> 210 <commit revision="0"> 211 <date>2008-08-19T16:50:53.400198Z</date> 212 </commit> 213 </wc-status> 214 </entry> 215 ''' 216 XMLWCStatus.fromstring(xml, path1) 217 218 def test_status_wrong_xml(self, path1): 219 # testing for XML without author - this used to raise an exception 220 xml = '<entry path="/home/jean/zope/venv/projectdb/parts/development-products/DataGridField">\n<wc-status item="incomplete" props="none" revision="784">\n</wc-status>\n</entry>' 221 st = XMLWCStatus.fromstring(xml, path1) 222 assert len(st.incomplete) == 1 223 224 def test_diff(self, path1): 225 p = path1 / 'anotherfile' 226 out = p.diff(rev=2) 227 assert out.find('hello') != -1 228 229 def test_blame(self, path1): 230 p = path1.join('samplepickle') 231 lines = p.blame() 232 assert sum([l[0] for l in lines]) == len(lines) 233 for l1, l2 in zip(p.readlines(), [l[2] for l in lines]): 234 assert l1 == l2 235 assert [l[1] for l in lines] == ['hpk'] * len(lines) 236 p = path1.join('samplefile') 237 lines = p.blame() 238 assert sum([l[0] for l in lines]) == len(lines) 239 for l1, l2 in zip(p.readlines(), [l[2] for l in lines]): 240 assert l1 == l2 241 assert [l[1] for l in lines] == ['hpk'] * len(lines) 242 243 def test_join_abs(self, path1): 244 s = str(path1.localpath) 245 n = path1.join(s, abs=1) 246 assert path1 == n 247 248 def test_join_abs2(self, path1): 249 assert path1.join('samplefile', abs=1) == path1.join('samplefile') 250 251 def test_str_gives_localpath(self, path1): 252 assert str(path1) == str(path1.localpath) 253 254 def test_versioned(self, path1): 255 assert path1.check(versioned=1) 256 # TODO: Why does my copy of svn think .svn is versioned? 257 #assert path1.join('.svn').check(versioned=0) 258 assert path1.join('samplefile').check(versioned=1) 259 assert not path1.join('notexisting').check(versioned=1) 260 notexisting = path1.join('hello').localpath 261 try: 262 notexisting.write("") 263 assert path1.join('hello').check(versioned=0) 264 finally: 265 notexisting.remove() 266 267 def test_listdir_versioned(self, path1): 268 assert path1.check(versioned=1) 269 p = path1.localpath.ensure("not_a_versioned_file") 270 l = [x.localpath 271 for x in path1.listdir(lambda x: x.check(versioned=True))] 272 assert p not in l 273 274 def test_nonversioned_remove(self, path1): 275 assert path1.check(versioned=1) 276 somefile = path1.join('nonversioned/somefile') 277 nonwc = py.path.local(somefile) 278 nonwc.ensure() 279 assert somefile.check() 280 assert not somefile.check(versioned=True) 281 somefile.remove() # this used to fail because it tried to 'svn rm' 282 283 def test_properties(self, path1): 284 try: 285 path1.propset('gaga', 'this') 286 assert path1.propget('gaga') == 'this' 287 # Comparing just the file names, because paths are unpredictable 288 # on Windows. (long vs. 8.3 paths) 289 assert path1.basename in [item.basename for item in 290 path1.status().prop_modified] 291 assert 'gaga' in path1.proplist() 292 assert path1.proplist()['gaga'] == 'this' 293 294 finally: 295 path1.propdel('gaga') 296 297 def test_proplist_recursive(self, path1): 298 s = path1.join('samplefile') 299 s.propset('gugu', 'that') 300 try: 301 p = path1.proplist(rec=1) 302 # Comparing just the file names, because paths are unpredictable 303 # on Windows. (long vs. 8.3 paths) 304 assert (path1 / 'samplefile').basename in [item.basename 305 for item in p] 306 finally: 307 s.propdel('gugu') 308 309 def test_long_properties(self, path1): 310 value = """ 311 vadm:posix : root root 0100755 312 Properties on 'chroot/dns/var/bind/db.net.xots': 313 """ 314 try: 315 path1.propset('gaga', value) 316 backvalue = path1.propget('gaga') 317 assert backvalue == value 318 #assert len(backvalue.split('\n')) == 1 319 finally: 320 path1.propdel('gaga') 321 322 323 def test_ensure(self, path1): 324 newpath = path1.ensure('a', 'b', 'c') 325 try: 326 assert newpath.check(exists=1, versioned=1) 327 newpath.write("hello") 328 newpath.ensure() 329 assert newpath.read() == "hello" 330 finally: 331 path1.join('a').remove(force=1) 332 333 def test_not_versioned(self, path1): 334 p = path1.localpath.mkdir('whatever') 335 f = path1.localpath.ensure('testcreatedfile') 336 try: 337 assert path1.join('whatever').check(versioned=0) 338 assert path1.join('testcreatedfile').check(versioned=0) 339 assert not path1.join('testcreatedfile').check(versioned=1) 340 finally: 341 p.remove(rec=1) 342 f.remove() 343 344 def test_lock_unlock(self, path1): 345 root = path1 346 somefile = root.join('somefile') 347 somefile.ensure(file=True) 348 # not yet added to repo 349 py.test.raises(Exception, 'somefile.lock()') 350 somefile.write('foo') 351 somefile.commit('test') 352 assert somefile.check(versioned=True) 353 somefile.lock() 354 try: 355 locked = root.status().locked 356 assert len(locked) == 1 357 assert locked[0].basename == somefile.basename 358 assert locked[0].dirpath().basename == somefile.dirpath().basename 359 #assert somefile.locked() 360 py.test.raises(Exception, 'somefile.lock()') 361 finally: 362 somefile.unlock() 363 #assert not somefile.locked() 364 locked = root.status().locked 365 assert locked == [] 366 py.test.raises(Exception, 'somefile,unlock()') 367 somefile.remove() 368 369 def test_commit_nonrecursive(self, path1): 370 somedir = path1.join('sampledir') 371 somedir.mkdir("subsubdir") 372 somedir.propset('foo', 'bar') 373 status = somedir.status() 374 assert len(status.prop_modified) == 1 375 assert len(status.added) == 1 376 377 somedir.commit('non-recursive commit', rec=0) 378 status = somedir.status() 379 assert len(status.prop_modified) == 0 380 assert len(status.added) == 1 381 382 somedir.commit('recursive commit') 383 status = somedir.status() 384 assert len(status.prop_modified) == 0 385 assert len(status.added) == 0 386 387 def test_commit_return_value(self, path1): 388 testfile = path1.join('test.txt').ensure(file=True) 389 testfile.write('test') 390 rev = path1.commit('testing') 391 assert type(rev) == int 392 393 anotherfile = path1.join('another.txt').ensure(file=True) 394 anotherfile.write('test') 395 rev2 = path1.commit('testing more') 396 assert type(rev2) == int 397 assert rev2 == rev + 1 398 399 #def test_log(self, path1): 400 # l = path1.log() 401 # assert len(l) == 3 # might need to be upped if more tests are added 402 403class XTestWCSvnCommandPathSpecial: 404 405 rooturl = 'http://codespeak.net/svn/py.path/trunk/dist/py.path/test/data' 406 #def test_update_none_rev(self, path1): 407 # path = tmpdir.join('checkouttest') 408 # wcpath = newpath(xsvnwc=str(path), url=path1url) 409 # try: 410 # wcpath.checkout(rev=2100) 411 # wcpath.update() 412 # assert wcpath.info().rev > 2100 413 # finally: 414 # wcpath.localpath.remove(rec=1) 415 416def test_parse_wcinfotime(): 417 assert (parse_wcinfotime('2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)') == 418 1149021926) 419 assert (parse_wcinfotime('2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)') == 420 1067287394) 421 422class TestInfoSvnWCCommand: 423 424 def test_svn_1_2(self, path1): 425 output = """ 426 Path: test_svnwc.py 427 Name: test_svnwc.py 428 URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py 429 Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada 430 Revision: 28137 431 Node Kind: file 432 Schedule: normal 433 Last Changed Author: jan 434 Last Changed Rev: 27939 435 Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006) 436 Text Last Updated: 2006-06-01 00:42:53 +0200 (Thu, 01 Jun 2006) 437 Properties Last Updated: 2006-05-23 11:54:59 +0200 (Tue, 23 May 2006) 438 Checksum: 357e44880e5d80157cc5fbc3ce9822e3 439 """ 440 path = py.path.local(__file__).dirpath().chdir() 441 try: 442 info = InfoSvnWCCommand(output) 443 finally: 444 path.chdir() 445 assert info.last_author == 'jan' 446 assert info.kind == 'file' 447 assert info.mtime == 1149021926.0 448 assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py' 449 assert info.time == 1149021926000000.0 450 assert info.rev == 28137 451 452 453 def test_svn_1_3(self, path1): 454 output = """ 455 Path: test_svnwc.py 456 Name: test_svnwc.py 457 URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py 458 Repository Root: http://codespeak.net/svn 459 Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada 460 Revision: 28124 461 Node Kind: file 462 Schedule: normal 463 Last Changed Author: jan 464 Last Changed Rev: 27939 465 Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006) 466 Text Last Updated: 2006-06-02 23:46:11 +0200 (Fri, 02 Jun 2006) 467 Properties Last Updated: 2006-06-02 23:45:28 +0200 (Fri, 02 Jun 2006) 468 Checksum: 357e44880e5d80157cc5fbc3ce9822e3 469 """ 470 path = py.path.local(__file__).dirpath().chdir() 471 try: 472 info = InfoSvnWCCommand(output) 473 finally: 474 path.chdir() 475 assert info.last_author == 'jan' 476 assert info.kind == 'file' 477 assert info.mtime == 1149021926.0 478 assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py' 479 assert info.rev == 28124 480 assert info.time == 1149021926000000.0 481 482 483def test_characters_at(): 484 py.test.raises(ValueError, "py.path.svnwc('/tmp/@@@:')") 485 486def test_characters_tilde(): 487 py.path.svnwc('/tmp/test~') 488 489 490class TestRepo: 491 def test_trailing_slash_is_stripped(self, path1): 492 # XXX we need to test more normalizing properties 493 url = path1.join("/") 494 assert path1 == url 495 496 #def test_different_revs_compare_unequal(self, path1): 497 # newpath = path1.new(rev=1199) 498 # assert newpath != path1 499 500 def test_exists_svn_root(self, path1): 501 assert path1.check() 502 503 #def test_not_exists_rev(self, path1): 504 # url = path1.__class__(path1url, rev=500) 505 # assert url.check(exists=0) 506 507 #def test_nonexisting_listdir_rev(self, path1): 508 # url = path1.__class__(path1url, rev=500) 509 # raises(py.error.ENOENT, url.listdir) 510 511 #def test_newrev(self, path1): 512 # url = path1.new(rev=None) 513 # assert url.rev == None 514 # assert url.strpath == path1.strpath 515 # url = path1.new(rev=10) 516 # assert url.rev == 10 517 518 #def test_info_rev(self, path1): 519 # url = path1.__class__(path1url, rev=1155) 520 # url = url.join("samplefile") 521 # res = url.info() 522 # assert res.size > len("samplefile") and res.created_rev == 1155 523 524 # the following tests are easier if we have a path class 525 def test_repocache_simple(self, path1): 526 repocache = svncommon.RepoCache() 527 repocache.put(path1.strpath, 42) 528 url, rev = repocache.get(path1.join('test').strpath) 529 assert rev == 42 530 assert url == path1.strpath 531 532 def test_repocache_notimeout(self, path1): 533 repocache = svncommon.RepoCache() 534 repocache.timeout = 0 535 repocache.put(path1.strpath, path1.rev) 536 url, rev = repocache.get(path1.strpath) 537 assert rev == -1 538 assert url == path1.strpath 539 540 def test_repocache_outdated(self, path1): 541 repocache = svncommon.RepoCache() 542 repocache.put(path1.strpath, 42, timestamp=0) 543 url, rev = repocache.get(path1.join('test').strpath) 544 assert rev == -1 545 assert url == path1.strpath 546 547 def _test_getreporev(self): 548 """ this test runs so slow it's usually disabled """ 549 old = svncommon.repositories.repos 550 try: 551 _repocache.clear() 552 root = path1.new(rev=-1) 553 url, rev = cache.repocache.get(root.strpath) 554 assert rev>=0 555 assert url == svnrepourl 556 finally: 557 repositories.repos = old 558