1#!/usr/bin/env python 2# 3# wc_tests.py: testing working-copy operations 4# 5# Subversion is a tool for revision control. 6# See http://subversion.apache.org for more information. 7# 8# ==================================================================== 9# Licensed to the Apache Software Foundation (ASF) under one 10# or more contributor license agreements. See the NOTICE file 11# distributed with this work for additional information 12# regarding copyright ownership. The ASF licenses this file 13# to you under the Apache License, Version 2.0 (the 14# "License"); you may not use this file except in compliance 15# with the License. You may obtain a copy of the License at 16# 17# http://www.apache.org/licenses/LICENSE-2.0 18# 19# Unless required by applicable law or agreed to in writing, 20# software distributed under the License is distributed on an 21# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22# KIND, either express or implied. See the License for the 23# specific language governing permissions and limitations 24# under the License. 25###################################################################### 26 27# General modules 28from __future__ import with_statement 29import shutil, stat, re, os, logging 30 31logger = logging.getLogger() 32 33# Our testing module 34import svntest 35from svntest import wc 36 37# (abbreviation) 38Skip = svntest.testcase.Skip_deco 39SkipUnless = svntest.testcase.SkipUnless_deco 40XFail = svntest.testcase.XFail_deco 41Issues = svntest.testcase.Issues_deco 42Issue = svntest.testcase.Issue_deco 43Wimp = svntest.testcase.Wimp_deco 44Item = wc.StateItem 45UnorderedOutput = svntest.verify.UnorderedOutput 46 47###################################################################### 48# Tests 49# 50# Each test must return on success or raise on failure. 51 52 53@XFail() 54@Issue(4193) 55@SkipUnless(svntest.main.is_posix_os) 56def status_through_unversioned_symlink(sbox): 57 """file status through unversioned symlink""" 58 59 sbox.build(read_only = True) 60 state = svntest.actions.get_virginal_state(sbox.wc_dir, 1) 61 os.symlink('A', sbox.ospath('Z')) 62 svntest.actions.run_and_verify_status(sbox.ospath('Z/mu'), state) 63 64@XFail() 65@Issue(4193) 66@SkipUnless(svntest.main.is_posix_os) 67def status_through_versioned_symlink(sbox): 68 """file status through versioned symlink""" 69 70 sbox.build(read_only = True) 71 state = svntest.actions.get_virginal_state(sbox.wc_dir, 1) 72 os.symlink('A', sbox.ospath('Z')) 73 sbox.simple_add('Z') 74 state.add({'Z': Item(status='A ')}) 75 svntest.actions.run_and_verify_status(sbox.ospath('Z/mu'), state) 76 77@XFail() 78@Issue(4193) 79@SkipUnless(svntest.main.is_posix_os) 80def status_with_symlink_in_path(sbox): 81 """file status with not-parent symlink""" 82 83 sbox.build(read_only = True) 84 state = svntest.actions.get_virginal_state(sbox.wc_dir, 1) 85 os.symlink('A', sbox.ospath('Z')) 86 svntest.actions.run_and_verify_status(sbox.ospath('Z/B/lambda'), state) 87 88@XFail() 89@Issue(4193) 90@SkipUnless(svntest.main.is_posix_os) 91def add_through_unversioned_symlink(sbox): 92 """add file through unversioned symlink""" 93 94 sbox.build(read_only = True) 95 os.symlink('A', sbox.ospath('Z')) 96 sbox.simple_append('A/kappa', 'xyz', True) 97 sbox.simple_add('Z/kappa') 98 99@XFail() 100@Issue(4193) 101@SkipUnless(svntest.main.is_posix_os) 102def add_through_versioned_symlink(sbox): 103 """add file through versioned symlink""" 104 105 sbox.build(read_only = True) 106 os.symlink('A', sbox.ospath('Z')) 107 sbox.simple_add('Z') 108 sbox.simple_append('A/kappa', 'xyz', True) 109 sbox.simple_add('Z/kappa') 110 111@XFail() 112@Issue(4193) 113@SkipUnless(svntest.main.is_posix_os) 114def add_with_symlink_in_path(sbox): 115 """add file with not-parent symlink""" 116 117 sbox.build(read_only = True) 118 os.symlink('A', sbox.ospath('Z')) 119 sbox.simple_append('A/B/kappa', 'xyz', True) 120 sbox.simple_add('Z/B/kappa') 121 122def is_posix_os_and_not_root(): 123 if not svntest.main.is_posix_os(): 124 return False 125 return os.getuid() != 0 126 127@Issue(4118) 128@SkipUnless(is_posix_os_and_not_root) 129def status_with_inaccessible_wc_db(sbox): 130 """inaccessible .svn/wc.db""" 131 132 sbox.build(read_only = True) 133 os.chmod(sbox.ospath(".svn/wc.db"), 0) 134 svntest.actions.run_and_verify_svn( 135 None, 136 r"[^ ]+ E155016: The working copy database at '.*' is corrupt", 137 "st", sbox.wc_dir) 138 139@Issue(4118) 140def status_with_corrupt_wc_db(sbox): 141 """corrupt .svn/wc.db""" 142 143 sbox.build(read_only = True) 144 with open(sbox.ospath(".svn/wc.db"), 'wb') as fd: 145 fd.write(b'\0' * 17) 146 svntest.actions.run_and_verify_svn( 147 None, 148 r"[^ ]+ E155016: The working copy database at '.*' is corrupt", 149 "st", sbox.wc_dir) 150 151@Issue(4118) 152def status_with_zero_length_wc_db(sbox): 153 """zero-length .svn/wc.db""" 154 155 sbox.build(read_only = True) 156 os.close(os.open(sbox.ospath(".svn/wc.db"), os.O_RDWR | os.O_TRUNC)) 157 svntest.actions.run_and_verify_svn( 158 None, 159 r"[^ ]+ E200030:", # SVN_ERR_SQLITE_ERROR 160 "st", sbox.wc_dir) 161 162@Issue(4118) 163def status_without_wc_db(sbox): 164 """missing .svn/wc.db""" 165 166 sbox.build(read_only = True) 167 os.remove(sbox.ospath(".svn/wc.db")) 168 svntest.actions.run_and_verify_svn( 169 None, 170 r"[^ ]+ E155016: The working copy database at '.*' is missing", 171 "st", sbox.wc_dir) 172 173@Issue(4118) 174@Skip() # FIXME: Test fails in-tree because it finds the source WC root 175def status_without_wc_db_and_entries(sbox): 176 """missing .svn/wc.db and .svn/entries""" 177 178 sbox.build(read_only = True) 179 os.remove(sbox.ospath(".svn/wc.db")) 180 os.remove(sbox.ospath(".svn/entries")) 181 svntest.actions.run_and_verify_svn2( 182 None, 183 r"[^ ]+ warning: W155007: '.*' is not a working copy", 184 0, "st", sbox.wc_dir) 185 186@Issue(4118) 187def status_with_missing_wc_db_and_maybe_valid_entries(sbox): 188 """missing .svn/wc.db, maybe valid .svn/entries""" 189 190 sbox.build(read_only = True) 191 with open(sbox.ospath(".svn/entries"), 'ab') as fd: 192 fd.write(b'something\n') 193 os.remove(sbox.ospath(".svn/wc.db")) 194 svntest.actions.run_and_verify_svn( 195 None, 196 r"[^ ]+ E155036:", # SVN_ERR_WC_UPGRADE_REQUIRED 197 "st", sbox.wc_dir) 198 199 200@Issue(4267) 201def cleanup_below_wc_root(sbox): 202 """cleanup from directory below WC root""" 203 204 sbox.build(read_only = True) 205 svntest.actions.lock_admin_dir(sbox.ospath(""), True) 206 svntest.actions.run_and_verify_svn(None, [], 207 "cleanup", sbox.ospath("A")) 208 209@SkipUnless(svntest.main.is_posix_os) 210@Issue(4383) 211def update_through_unversioned_symlink(sbox): 212 """update through unversioned symlink""" 213 214 sbox.build(read_only = True) 215 wc_dir = sbox.wc_dir 216 state = svntest.actions.get_virginal_state(wc_dir, 1) 217 symlink = sbox.get_tempname() 218 os.symlink(os.path.abspath(sbox.wc_dir), symlink) 219 expected_output = [] 220 expected_disk = [] 221 expected_status = [] 222 # Subversion 1.8.0 crashes when updating a working copy through a symlink 223 svntest.actions.run_and_verify_update(wc_dir, expected_output, 224 expected_disk, expected_status, 225 [], True, symlink) 226 227@Issue(3549) 228def cleanup_unversioned_items(sbox): 229 """cleanup --remove-unversioned / --remove-ignored""" 230 231 sbox.build(read_only = True) 232 wc_dir = sbox.wc_dir 233 234 # create some unversioned items 235 os.mkdir(sbox.ospath('dir1')) 236 os.mkdir(sbox.ospath('dir2')) 237 contents = "This is an unversioned file\n." 238 svntest.main.file_write(sbox.ospath('dir1/dir1_child1'), contents) 239 svntest.main.file_write(sbox.ospath('dir2/dir2_child1'), contents) 240 os.mkdir(sbox.ospath('dir2/foo_child2')) 241 svntest.main.file_write(sbox.ospath('file_foo'), contents), 242 os.mkdir(sbox.ospath('dir_foo')) 243 svntest.main.file_write(sbox.ospath('dir_foo/foo_child1'), contents) 244 os.mkdir(sbox.ospath('dir_foo/foo_child2')) 245 # a file that matches a default ignore pattern 246 svntest.main.file_write(sbox.ospath('foo.o'), contents) 247 248 # ignore some of the unversioned items 249 sbox.simple_propset('svn:ignore', '*_foo', '.') 250 251 os.chdir(wc_dir) 252 253 expected_output = [ 254 ' M .\n', 255 '? dir1\n', 256 '? dir2\n', 257 ] 258 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 259 [], 'status') 260 expected_output += [ 261 'I dir_foo\n', 262 'I file_foo\n', 263 'I foo.o\n', 264 ] 265 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 266 [], 'status', '--no-ignore') 267 268 expected_output = [ 269 'D dir1\n', 270 'D dir2\n', 271 ] 272 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 273 [], 'cleanup', '--remove-unversioned') 274 expected_output = [ 275 ' M .\n', 276 'I dir_foo\n', 277 'I file_foo\n', 278 'I foo.o\n', 279 ] 280 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 281 [], 'status', '--no-ignore') 282 283 # remove ignored items, with an empty global-ignores list 284 expected_output = [ 285 'D dir_foo\n', 286 'D file_foo\n', 287 ] 288 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 289 [], 'cleanup', '--remove-ignored', 290 '--config-option', 291 'config:miscellany:global-ignores=') 292 293 # the file matching global-ignores should still be present 294 expected_output = [ 295 ' M .\n', 296 'I foo.o\n', 297 ] 298 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 299 [], 'status', '--no-ignore') 300 301 # un-ignore the file matching global ignores, making it unversioned, 302 # and remove it with --remove-unversioned 303 expected_output = [ 304 'D foo.o\n', 305 ] 306 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 307 [], 'cleanup', '--remove-unversioned', 308 '--config-option', 309 'config:miscellany:global-ignores=') 310 expected_output = [ 311 ' M .\n', 312 ] 313 svntest.actions.run_and_verify_svn(expected_output, 314 [], 'status', '--no-ignore') 315 316def cleanup_unversioned_items_in_locked_wc(sbox): 317 """cleanup unversioned items in locked WC should fail""" 318 319 sbox.build(read_only = True) 320 321 contents = "This is an unversioned file\n." 322 svntest.main.file_write(sbox.ospath('unversioned_file'), contents) 323 324 svntest.actions.lock_admin_dir(sbox.ospath(""), True) 325 for option in ['--remove-unversioned', '--remove-ignored']: 326 svntest.actions.run_and_verify_svn(None, 327 "svn: E155004: Working copy locked;.*", 328 "cleanup", option, 329 sbox.ospath("")) 330 331def cleanup_dir_external(sbox): 332 """cleanup --include-externals""" 333 334 sbox.build(read_only = True) 335 336 # configure a directory external 337 sbox.simple_propset("svn:externals", "^/A A_ext", ".") 338 sbox.simple_update() 339 340 svntest.actions.lock_admin_dir(sbox.ospath("A_ext"), True) 341 svntest.actions.run_and_verify_svn(["Performing cleanup on external " + 342 "item at '%s'.\n" % sbox.ospath("A_ext")], 343 [], "cleanup", '--include-externals', 344 sbox.ospath("")) 345 346@Issue(4390) 347def checkout_within_locked_wc(sbox): 348 """checkout within a locked working copy""" 349 350 sbox.build(read_only = True) 351 352 # lock working copy and create outstanding work queue items 353 svntest.actions.lock_admin_dir(sbox.ospath(""), True, True) 354 expected_output = [ 355 "A %s\n" % sbox.ospath("nested-wc/alpha"), 356 "A %s\n" % sbox.ospath("nested-wc/beta"), 357 "Checked out revision 1.\n" 358 ] 359 svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), 360 [], "checkout", sbox.repo_url + '/A/B/E', 361 sbox.ospath("nested-wc")) 362 363 364######################################################################## 365# Run the tests 366 367# list all tests here, starting with None: 368test_list = [ None, 369 status_through_unversioned_symlink, 370 status_through_versioned_symlink, 371 status_with_symlink_in_path, 372 add_through_unversioned_symlink, 373 add_through_versioned_symlink, 374 add_with_symlink_in_path, 375 status_with_inaccessible_wc_db, 376 status_with_corrupt_wc_db, 377 status_with_zero_length_wc_db, 378 status_without_wc_db, 379 status_without_wc_db_and_entries, 380 status_with_missing_wc_db_and_maybe_valid_entries, 381 cleanup_below_wc_root, 382 update_through_unversioned_symlink, 383 cleanup_unversioned_items, 384 cleanup_unversioned_items_in_locked_wc, 385 cleanup_dir_external, 386 checkout_within_locked_wc, 387 ] 388 389if __name__ == '__main__': 390 svntest.main.run_tests(test_list) 391 # NOTREACHED 392 393 394### End of file. 395