1#!/bin/sh
2#
3# Copyright (c) 2010 Hudson River Trading LLC
4# Written by: John H. Baldwin <jhb@FreeBSD.org>
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28
29# Various regression tests to test the -F flag to the 'update' command.
30
31FAILED=no
32WORKDIR=work
33
34usage()
35{
36	echo "Usage: fbsdid.sh [-s script] [-w workdir]"
37	exit 1
38}
39
40# Allow the user to specify an alternate work directory or script.
41COMMAND=etcupdate
42while getopts "s:w:" option; do
43	case $option in
44		s)
45			COMMAND="sh $OPTARG"
46			;;
47		w)
48			WORKDIR=$OPTARG
49			;;
50		*)
51			echo
52			usage
53			;;
54	esac
55done
56shift $((OPTIND - 1))
57if [ $# -ne 0 ]; then
58	usage
59fi
60
61CONFLICTS=$WORKDIR/conflicts
62OLD=$WORKDIR/old
63NEW=$WORKDIR/current
64TEST=$WORKDIR/test
65
66# Store a FreeBSD ID string in a specified file.  The first argument
67# is the file, the remaining arguments are the comment to use.
68store_id()
69{
70	local file
71
72	file=$1
73	shift
74
75	echo -n '# $FreeBSD' >> $file
76	echo -n "$@" >> $file
77	echo '$' >> $file
78}
79
80# These tests deal with FreeBSD ID string conflicts.  We run the test
81# twice, once without -F and once with -F.
82build_trees()
83{
84	local i
85
86	rm -rf $OLD $NEW $TEST $CONFLICTS
87	mkdir -p $OLD $NEW $TEST
88
89	# remove: Remove a file where the only local difference is a
90	# change in the FreeBSD ID string.
91	store_id $OLD/remove
92	store_id $TEST/remove ": head/remove 12345 jhb "
93
94	# old: Modify a file where the only local difference between
95	# the old and test files is a change in the FreeBSD ID string.
96	store_id $OLD/old ": src/old,v 1.1 jhb Exp "
97	store_id $NEW/old ": head/old 12345 jhb "
98	store_id $TEST/old ": head/old 12000 jhb "
99	for i in $OLD $TEST; do
100		cat >> $i/old <<EOF
101
102an old file
103EOF
104	done
105	cat >> $NEW/old <<EOF
106
107a new file
108EOF
109
110	# already: Modify a file where the local file already matches
111	# the new file except for a change in the FreeBSD ID string.
112	store_id $OLD/already ": src/already,v 1.1 jhb Exp "
113	store_id $NEW/already ": head/already 12345 jhb "
114	store_id $TEST/already ": src/already,v 1.2 jhb Exp "
115	cat >> $OLD/already <<EOF
116
117another old file
118EOF
119	for i in $NEW $TEST; do
120		cat >> $i/already <<EOF
121
122another new file
123EOF
124	done
125
126	# add: Add a file that already exists where the only local
127	# difference is a change in the FreeBSD ID string.
128	store_id $NEW/add ": head/add 12345 jhb "
129	store_id $TEST/add ""
130
131	# conflict: Modify a file where the local file has a different
132	# FreeBSD ID string.  This should still generate a conflict
133	# even in the -F case.
134	store_id $OLD/conflict ": head/conflict 12000 jhb "
135	store_id $NEW/conflict ": head/conflict 12345 jhb "
136	store_id $TEST/conflict ""
137	cat >> $OLD/conflict <<EOF
138
139this is the old file
140EOF
141	cat >> $NEW/conflict <<EOF
142
143this is the new file
144EOF
145	cat >> $TEST/conflict <<EOF
146
147this is the local file
148EOF
149
150	# local: A file with local modifications has a different
151	# FreeBSD ID string and the only differences between the old
152	# and new versions are a change in the FreeBSD ID string.
153	# This will just update the FreeBSD ID string in the -F case.
154	for i in $OLD $NEW $TEST; do
155		cat >> $i/local <<EOF
156# Some leading text
157#
158EOF
159	done
160
161	store_id $OLD/local ": head/local 12000 jhb "
162	store_id $NEW/local ": head/local 12345 jhb "
163	store_id $TEST/local ": src/local,v 1.5 jhb Exp "
164
165	for i in $OLD $NEW $TEST; do
166		cat >> $i/local <<EOF
167
168this is a file
169EOF
170	done
171
172	cat >> $TEST/local <<EOF
173
174these are some local mods to the file
175EOF
176
177	# local-already: A file with local modifications has the same
178	# FreeBSD ID string as the new version of the file and the
179	# only differences between the old and new versions are a
180	# change in the FreeBSD ID string.  Nothing should happen in
181	# the -F case.
182	store_id $OLD/local-already ": head/local 12000 jhb "
183	for i in $NEW $TEST; do
184		store_id $i/local-already ": head/local 12345 jhb "
185	done
186
187	for i in $OLD $NEW $TEST; do
188		cat >> $i/local-already <<EOF
189
190this is a file
191EOF
192	done
193
194	cat >> $TEST/local-already <<EOF
195
196these are some local mods to the file
197EOF
198
199	# local-remove: A file removed locally changed it's FreeBSD ID
200	# but nothing else
201	store_id $OLD/local-remove ": head/local-remove 12000 jhb "
202	store_id $NEW/local-remove ": head/local-remove 12345 jhb "
203	for i in $OLD $NEW; do
204		cat >> $i/local-remove <<EOF
205
206this is a file
207EOF
208	done
209}
210
211# $1 - relative path to file that should be missing from TEST
212missing()
213{
214	if [ -e $TEST/$1 -o -L $TEST/$1 ]; then
215		echo "File $1 should be missing"
216		FAILED=yes
217	fi
218}
219
220# $1 - relative path to file that should be present in TEST
221present()
222{
223	if ! [ -e $TEST/$1 -o -L $TEST/$1 ]; then
224		echo "File $1 should be present"
225		FAILED=yes
226	fi
227}
228
229# $1 - relative path to regular file that should be present in TEST
230# $2 - optional string that should match file contents
231# $3 - optional MD5 of the flie contents, overrides $2 if present
232file()
233{
234	local contents sum
235
236	if ! [ -f $TEST/$1 ]; then
237		echo "File $1 should be a regular file"
238		FAILED=yes
239	elif [ $# -eq 2 ]; then
240		contents=`cat $TEST/$1`
241		if [ "$contents" != "$2" ]; then
242			echo "File $1 has wrong contents"
243			FAILED=yes
244		fi
245	elif [ $# -eq 3 ]; then
246		sum=`md5 -q $TEST/$1`
247		if [ "$sum" != "$3" ]; then
248			echo "File $1 has wrong contents"
249			FAILED=yes
250		fi
251	fi
252}
253
254# $1 - relative path to a regular file that should have a conflict
255# $2 - optional MD5 of the conflict file contents
256conflict()
257{
258	local sum
259
260	if ! [ -f $CONFLICTS/$1 ]; then
261		echo "File $1 missing conflict"
262		FAILED=yes
263	elif [ $# -gt 1 ]; then
264		sum=`md5 -q $CONFLICTS/$1`
265		if [ "$sum" != "$2" ]; then
266			echo "Conflict $1 has wrong contents"
267			FAILED=yes
268		fi
269	fi
270}
271
272# $1 - relative path to a regular file that should not have a conflict
273noconflict()
274{
275	if [ -f $CONFLICTS/$1 ]; then
276		echo "File $1 should not have a conflict"
277		FAILED=yes
278	fi
279}
280
281if [ `id -u` -ne 0 ]; then
282	echo "must be root"
283	exit 0
284fi
285
286if [ -r /etc/etcupdate.conf ]; then
287	echo "WARNING: /etc/etcupdate.conf settings may break some tests."
288fi
289
290# First run the test without -F.
291
292build_trees
293
294$COMMAND -r -d $WORKDIR -D $TEST > $WORKDIR/test.out
295
296cat > $WORKDIR/correct.out <<EOF
297  C /already
298  C /conflict
299  C /local
300  M /local-already
301  C /old
302  C /add
303Warnings:
304  Modified regular file remains: /remove
305  Removed file changed: /local-remove
306EOF
307
308echo "Differences for regular:"
309diff -u -L "correct" $WORKDIR/correct.out -L "test" $WORKDIR/test.out \
310    || FAILED=yes
311
312file /remove "" 1bb4776213af107077be78fead8a351c
313file /old "" 2f799a7addc4132563ef9b44adc66157
314conflict /old 8441be64a1540f2ff584015279682425
315file /already "" aa53bd506f65d01d766e7ba028585e1d
316conflict /already f44105abb1fa3293e95c5d77e428d418
317file /add "" 1dc8c617e541d1fd1b4c70212f71d8ae
318conflict /add f99081e0da9a07f3cfebb430c0414941
319file /conflict "" dc27978df125b0daeb7d9b93265f03fd
320conflict /conflict 868452f666fea1c60ffb918ad9ad9607
321file /local "" aa33e614b5e749449f230e2a2b0072eb
322conflict /local 3df93e64043c8e348fc625b93ea220f4
323file /local-already "" 0298b958a603049f45ae6a109c4f7fea
324missing /local-remove
325
326# Now test with -F.
327
328build_trees
329
330$COMMAND -rF -d $WORKDIR -D $TEST > $WORKDIR/testF.out
331
332cat > $WORKDIR/correctF.out <<EOF
333  D /remove
334  U /already
335  C /conflict
336  M /local
337  U /old
338  U /add
339EOF
340
341echo "Differences for -F:"
342diff -u -L "correct" $WORKDIR/correctF.out -L "test" $WORKDIR/testF.out \
343    || FAILED=yes
344
345missing /remove
346file /old "" 6a9f34f109d94406a4de3bc5d72de259
347noconflict /old
348file /already "" 21f4eca3aacc702c49878c8da7afd3d0
349noconflict /already
350file /add "" 0208bd647111fedf6318511712ab9e97
351noconflict /add
352file /conflict "" dc27978df125b0daeb7d9b93265f03fd
353conflict /conflict 868452f666fea1c60ffb918ad9ad9607
354file /local "" 3ed5a35e380c8a93fb5f599d4c052713
355file /local-already "" 0298b958a603049f45ae6a109c4f7fea
356missing /local-remove
357
358# Now test with -F and -A forcing all installs.  (-A should have
359# precedence over -F)
360
361build_trees
362
363$COMMAND -A '/*' -rF -d $WORKDIR -D $TEST > $WORKDIR/testAF.out
364
365cat > $WORKDIR/correctAF.out <<EOF
366  D /remove
367  U /already
368  U /conflict
369  U /local
370  U /local-already
371  A /local-remove
372  U /old
373  U /add
374EOF
375
376echo "Differences for -A '/*' -F:"
377diff -u -L "correct" $WORKDIR/correctAF.out -L "test" $WORKDIR/testAF.out \
378    || FAILED=yes
379
380missing /remove
381file /old "" 6a9f34f109d94406a4de3bc5d72de259
382noconflict /old
383file /already "" 21f4eca3aacc702c49878c8da7afd3d0
384noconflict /already
385file /add "" 0208bd647111fedf6318511712ab9e97
386noconflict /add
387file /conflict "" 75ee141c4136beaf14e39de92efa84e4
388noconflict /conflict
389file /local "" 6a8fc5c2755b7a49015089f5e1dbe092
390file /local-already "" 49045f8b51542dd634655301cd296f66
391file /local-remove "" 5c38322efed4014797d7127f5c652d9d
392
393[ "${FAILED}" = no ]
394