1#!/usr/bin/env python 2# 3# history_tests.py: testing history-tracing code 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 28import os 29 30# Our testing module 31import svntest 32from svntest import wc 33 34# (abbreviation) 35Skip = svntest.testcase.Skip_deco 36SkipUnless = svntest.testcase.SkipUnless_deco 37XFail = svntest.testcase.XFail_deco 38Issues = svntest.testcase.Issues_deco 39Issue = svntest.testcase.Issue_deco 40Wimp = svntest.testcase.Wimp_deco 41Item = wc.StateItem 42 43###################################################################### 44# Tests 45# 46# Each test must return on success or raise on failure. 47 48#---------------------------------------------------------------------- 49 50def cat_traces_renames(sbox): 51 "verify that 'svn cat' traces renames" 52 53 sbox.build() 54 wc_dir = sbox.wc_dir 55 rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho') 56 pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi') 57 bloo_path = os.path.join(wc_dir, 'A', 'D', 'G', 'bloo') 58 59 # rename rho to bloo. commit r2. 60 svntest.main.run_svn(None, 'mv', rho_path, bloo_path) 61 62 expected_output = svntest.wc.State(wc_dir, { 63 'A/D/G/rho' : Item(verb='Deleting'), 64 'A/D/G/bloo' : Item(verb='Adding') 65 }) 66 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 67 expected_status.remove('A/D/G/rho') 68 expected_status.add({ 'A/D/G/bloo' : 69 Item(wc_rev=2, status=' ') }) 70 71 svntest.actions.run_and_verify_commit(wc_dir, 72 expected_output, 73 expected_status) 74 75 # rename pi to rho. commit r3. 76 svntest.main.run_svn(None, 'mv', pi_path, rho_path) 77 78 # svn cat -r1 rho --> should show pi's contents. 79 svntest.actions.run_and_verify_svn([ "This is the file 'pi'.\n"], [], 80 'cat', '-r', '1', rho_path) 81 82 expected_output = svntest.wc.State(wc_dir, { 83 'A/D/G/pi' : Item(verb='Deleting'), 84 'A/D/G/rho' : Item(verb='Adding') 85 }) 86 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 87 expected_status.remove('A/D/G/pi') 88 expected_status.tweak('A/D/G/rho', wc_rev=3) 89 expected_status.add({ 'A/D/G/bloo' : 90 Item(wc_rev=2, status=' ') }) 91 92 svntest.actions.run_and_verify_commit(wc_dir, 93 expected_output, 94 expected_status) 95 96 # update whole wc to HEAD 97 expected_output = svntest.wc.State(wc_dir, { }) # no output 98 expected_status.tweak(wc_rev=3) 99 expected_disk = svntest.main.greek_state.copy() 100 expected_disk.remove('A/D/G/pi', 'A/D/G/rho') 101 expected_disk.add({ 102 'A/D/G/rho' : Item("This is the file 'pi'.\n"), 103 }) 104 expected_disk.add({ 105 'A/D/G/bloo' : Item("This is the file 'rho'.\n"), 106 }) 107 svntest.actions.run_and_verify_update(wc_dir, 108 expected_output, 109 expected_disk, 110 expected_status) 111 112 # 'svn cat bloo' --> should show rho's contents. 113 svntest.actions.run_and_verify_svn([ "This is the file 'rho'.\n"], [], 114 'cat', bloo_path) 115 116 # svn cat -r1 bloo --> should still show rho's contents. 117 svntest.actions.run_and_verify_svn([ "This is the file 'rho'.\n"], [], 118 'cat', '-r', '1', bloo_path) 119 120 # svn cat -r1 rho --> should show pi's contents. 121 svntest.actions.run_and_verify_svn([ "This is the file 'pi'.\n"], [], 122 'cat', '-r', '1', rho_path) 123 124 # svn up -r1 125 svntest.actions.run_and_verify_svn(None, [], 'up', '-r', '1', wc_dir) 126 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 127 svntest.actions.run_and_verify_status(wc_dir, expected_status) 128 129 # svn cat -rHEAD rho --> should see 'unrelated object' error. 130 svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, 131 'cat', '-r', 'HEAD', rho_path) 132 133@Issue(1970) 134def cat_avoids_false_identities(sbox): 135 "verify that 'svn cat' avoids false identities" 136 137 sbox.build() 138 wc_dir = sbox.wc_dir 139 140 # Issue #1970 141 # 142 # Highlight a bug in the client side use of the repository's 143 # location searching algorithm. 144 # 145 # The buggy history-following algorithm determines the paths that a 146 # line of history would be *expected to be* found in a given revision, 147 # but doesn't treat copies as gaps in the historical sequence. If 148 # some other object fills those gaps at the same expected path, the 149 # client will find the wrong object. 150 # 151 # In the recipe below, iota gets created in r1. In r2, it is 152 # deleted and replaced with an unrelated object at the same path. 153 # In r3, the interloper is deleted. In r4, the original iota is 154 # resurrected via a copy from r1. 155 # 156 # ,- - - - - - --. 157 # o---| o---| o o-----> 158 # 159 # | | | | 160 # r1 r2 r3 r4 161 # 162 # In a working copy at r4, running 163 # 164 # $ svn cat -r2 iota 165 # 166 # should result in an error, but with the bug it instead cats the r2 167 # interloper. 168 # 169 # To reassure yourself that that's wrong, recall that the above 170 # command is equivalent to 171 # 172 # $ svn cat -r2 iota@4 173 # 174 # Now do you see the evil that lies within us? 175 176 iota_path = os.path.join(wc_dir, 'iota') 177 iota_url = sbox.repo_url + '/iota' 178 179 # r2 180 svntest.main.run_svn(None, 'del', iota_path) 181 svntest.main.file_append(iota_path, "YOU SHOULD NOT SEE THIS\n") 182 svntest.main.run_svn(None, 'add', iota_path) 183 sbox.simple_commit(message='log msg') 184 svntest.main.run_svn(None, 'up', wc_dir) 185 186 # r3 187 svntest.main.run_svn(None, 'del', iota_path) 188 sbox.simple_commit(message='log msg') 189 svntest.main.run_svn(None, 'up', wc_dir) 190 191 # r4 192 svntest.main.run_svn(None, 'cp', iota_url + '@1', wc_dir) 193 sbox.simple_commit(message='log msg') 194 svntest.main.run_svn(None, 'up', wc_dir) 195 196 # 'svn cat -r2 iota' should error, because the line of history 197 # currently identified by /iota did not exist in r2, even though a 198 # totally unrelated file of the same name did. 199 svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, 200 'cat', '-r', '2', iota_path) 201 202 203######################################################################## 204# Run the tests 205 206# list all tests here, starting with None: 207test_list = [ None, 208 cat_traces_renames, 209 cat_avoids_false_identities, 210 ] 211 212if __name__ == '__main__': 213 svntest.main.run_tests(test_list) 214 # NOTREACHED 215 216 217### End of file. 218