1# 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27 28atf_test_case badfuzz 29badfuzz_head() 30{ 31 atf_set "descr" "Test for patch(1) erroneously fuzzing away action lines" 32} 33badfuzz_body() 34{ 35 # PR 250511 demonstrates a scenario where patch(1) will happily apply a 36 # patch into the wrong location if we have some lines that are still 37 # similar in the trailing context. In the following example, it would 38 # actually replace the underscore before the second series of B\nC\nO 39 # with "Z", when the patch should have been rejected instead. 40 printf "A\nB\nC\nO\n_\nB\nC\nO\n" > file.orig 41 printf "Z\nB\nC\nO\n_\nB\nC\nO\n" > file 42 printf "OK\nDIFF1\nDIFF2\n\n_\nB\nC\nO\n" > file.newer 43 44 atf_check -s not-exit:0 -o save:file.patch diff -u3 file.orig file 45 atf_check -s not-exit:0 -o not-empty patch file.newer file.patch 46} 47 48atf_test_case basic 49basic_body() 50{ 51 printf "a\nb\nc\nd\ne\nf\ng\nh\ni\n" > foo_full 52 printf "a\nb\nc\n" > foo_start 53 printf "g\nh\ni\n" > foo_end 54 printf "d\ne\nf\n" > foo_middle 55 56 diff -u foo_start foo_full > foo_start2full.diff 57 diff -u foo_end foo_full > foo_end2full.diff 58 diff -u foo_middle foo_full > foo_mid2full.diff 59 60 # Check lengths... each should have all 9 lines + 3 line header 61 atf_check -o inline:"12" -x \ 62 "cat foo_start2full.diff | wc -l | tr -d '[:space:]'" 63 atf_check -o inline:"12" -x \ 64 "cat foo_end2full.diff | wc -l | tr -d '[:space:]'" 65 atf_check -o inline:"12" -x \ 66 "cat foo_mid2full.diff | wc -l | tr -d '[:space:]'" 67 68 # Apply the patch! Should succeed 69 atf_check -o ignore patch foo_start foo_start2full.diff \ 70 -o foo_start2full 71 atf_check -o ignore patch foo_end foo_end2full.diff \ 72 -o foo_end2full 73 atf_check -o ignore patch foo_middle foo_mid2full.diff \ 74 -o foo_mid2full 75 76 # And these should all produce equivalent to the original full 77 atf_check -o ignore diff foo_start2full foo_full 78 atf_check -o ignore diff foo_end2full foo_full 79 atf_check -o ignore diff foo_mid2full foo_full 80} 81 82atf_test_case limited_ctx 83limited_ctx_head() 84{ 85 atf_set "descr" "Verify correct behavior with limited context (PR 74127)" 86} 87limited_ctx_body() 88{ 89 90 # First; PR74127-repro.diff should not have applied, but it instead 91 # assumed a match and added the modified line at the offset specified... 92 atf_check -s not-exit:0 -o ignore -e ignore patch -o _.out \ 93 "$(atf_get_srcdir)/PR74127.in" \ 94 "$(atf_get_srcdir)/PR74127-repro.diff" 95 96 # Let's extend that and make sure a similarly ill-contexted diff does 97 # not apply even with the correct line number 98 atf_check -s not-exit:0 -o ignore -e ignore patch -o _.out \ 99 "$(atf_get_srcdir)/PR74127.in" \ 100 "$(atf_get_srcdir)/PR74127-line.diff" 101 102 # Correct line number and correct old line should always work 103 atf_check -o ignore -e ignore patch -o _.out \ 104 "$(atf_get_srcdir)/PR74127.in" \ 105 "$(atf_get_srcdir)/PR74127-good.diff" 106} 107 108atf_test_case file_creation 109file_creation_body() 110{ 111 112 echo "x" > foo 113 diff -u /dev/null foo > foo.diff 114 rm foo 115 116 atf_check -x "patch -s < foo.diff" 117 atf_check -o ignore stat foo 118} 119 120# This test is motivated by long-standing bugs that occasionally slip by in 121# commits. If a file is created by a diff, patch(1) will happily duplicate the 122# contents as many times as you apply the diff. It should instead detect that 123# a source of /dev/null creates the file, so it shouldn't exist. Furthermore, 124# the reverse of creation is deletion -- hence the next test, which ensures that 125# the file is removed if it's empty once the patch is reversed. The size checks 126# are scattered throughout to make sure that we didn't get some kind of false 127# error, and the first size check is merely a sanity check that should be 128# trivially true as this is executed in a sandbox. 129atf_test_case file_nodupe 130file_nodupe_body() 131{ 132 133 echo "x" > foo 134 diff -u /dev/null foo > foo.diff 135 136 atf_check -o inline:"2\n" stat -f "%z" foo 137 atf_check -s not-exit:0 -o ignore -x "patch -Ns < foo.diff" 138 atf_check -o inline:"2\n" stat -f "%z" foo 139 atf_check -s not-exit:0 -o ignore -x "patch -fs < foo.diff" 140 atf_check -o inline:"2\n" stat -f "%z" foo 141} 142 143atf_test_case file_removal 144file_removal_body() 145{ 146 147 echo "x" > foo 148 diff -u /dev/null foo > foo.diff 149 150 # Check that the file is removed completely if it was sourced from 151 # /dev/null 152 atf_check -x "patch -Rs < foo.diff" 153 atf_check -s not-exit:0 -e ignore stat foo 154 155 # But if it had been modified, we'll only remove the portion that the 156 # patch would have created. This makes us compatible with GNU patch's 157 # behavior, at least. Whether that is the sane action or not is a 158 # question for further study, and then this comment may be removed. 159 printf "x\ny\n" > foo 160 atf_check -x "patch -Rs < foo.diff" 161 atf_check -o inline:"y\n" cat foo 162} 163 164atf_test_case namespace 165namespace_head() 166{ 167 atf_set "descr" "Test that patch(1) handles files with spaces in the name" 168} 169namespace_body() 170{ 171 echo "ABC" > "with spaces.orig" 172 echo "ZYX" > "with spaces" 173 174 atf_check -s not-exit:0 -o save:spaces.diff \ 175 diff -u "with spaces.orig" "with spaces" 176 177 atf_check mv "with spaces.orig" "with spaces" 178 atf_check -o not-empty patch < spaces.diff 179} 180 181atf_test_case plinelen 182plinelen_body() 183{ 184 hello="$(jot -b hello -s, 20000 | tee foo.txt)" 185 cp foo.txt bar.txt 186 echo "world" >>bar.txt 187 cat >foo.diff <<EOF 188--- foo.txt.orig 189+++ foo.txt 190@@ -1,1 +1,2 @@ 191 $hello 192+world 193EOF 194 atf_check -o match:"Hunk #1 succeeded" \ 195 patch <foo.diff 196 atf_check -o file:bar.txt cat foo.txt 197} 198 199atf_init_test_cases() 200{ 201 atf_add_test_case badfuzz 202 atf_add_test_case basic 203 atf_add_test_case limited_ctx 204 atf_add_test_case file_creation 205 atf_add_test_case file_nodupe 206 atf_add_test_case file_removal 207 atf_add_test_case namespace 208 atf_add_test_case plinelen 209} 210