1#!/bin/sh
2#
3# cmptest @(#)cmptest.sh	1.15 20/08/25 Copyright 2015-2020 J. Schilling
4#
5# Usage: cmptest	---> runs 1000 test loops
6#	 cmptest #	---> runs # test loops
7#
8# A random file test with patch -DXXX usage.
9# A file is generated, then modified.
10# Then a patch file is created from original and new file.
11# A reference patch implementation is called with -DXXX and the output
12# is compared to the output from out test patch. Gpatch is buggy, so we
13# sometimes need to use our test patch as reference.
14#
15# Finally cpp is used to check whether original and new file can be
16# reconstructed from the #ifdef XXX ... #else ... #endif parts.
17#
18# The diff type is random, this allows to check all diff types.
19#
20
21[ "$NO_RANDOM" = TRUE ] && exit
22
23: ${AWK=/usr/bin/nawk}
24#$AWK 'BEGIN {print rand()}' < /dev/null > /dev/null 2> /dev/null || AWK=/usr/bin/nawk
25$AWK 'BEGIN {print rand()}' < /dev/null > /dev/null 2> /dev/null || AWK=/usr/bin/gawk
26$AWK 'BEGIN {print rand()}' < /dev/null > /dev/null 2> /dev/null || AWK=/usr/bin/awk
27$AWK 'BEGIN {print rand()}' < /dev/null > /dev/null 2> /dev/null || AWK=nawk
28$AWK 'BEGIN {print rand()}' < /dev/null > /dev/null 2> /dev/null || AWK=gawk
29$AWK 'BEGIN {print rand()}' < /dev/null > /dev/null 2> /dev/null || AWK=awk
30
31: ${CPP=/lib/cpp}
32$CPP < /dev/null > /dev/null 2> /dev/null || CPP=/usr/lib/cpp
33$CPP < /dev/null > /dev/null 2> /dev/null || CPP=/usr/ccs/lib/cpp
34$CPP < /dev/null > /dev/null 2> /dev/null || CPP=/usr/bin/cpp
35$CPP < /dev/null > /dev/null 2> /dev/null || CPP=/bin/cpp
36$CPP < /dev/null > /dev/null 2> /dev/null || CPP=/opt/schily/lib/cpp
37$CPP < /dev/null > /dev/null 2> /dev/null || CPP=../../../cpp/OBJ/"`../../../conf/oarch.sh`"/cpp
38$CPP < /dev/null > /dev/null 2> /dev/null || CPP=/usr/sfw/bin/cpp
39$CPP < /dev/null > /dev/null 2> /dev/null || CPP=/usr/gnu/bin/cpp
40$CPP < /dev/null > /dev/null 2> /dev/null || CPP=cpp
41
42trap 'cleanup; exit' EXIT INT HUP
43
44cleanup() {
45	rm -f saved_orig changed patch_file expected original original.* failure xo xm xof xmf xef
46}
47
48rrand() {
49	$AWK '
50	function random(low, range) {
51		return int(range * rand()) + low
52	}
53
54	BEGIN {
55		base = ARGV[1]
56		max = ARGV[2]
57		seed = ARGV[3]
58
59		srand()		# Initialze with current time
60		s = srand()	# get previous seed
61		s = s + seed	# Current time + seed
62		srand(s)	# Better new seed
63
64		x = random(base, max)
65		print x
66	}
67	' "$@"
68}
69
70makefile() {
71	$AWK '
72	BEGIN {
73		nflines = ARGV[1]
74		for (i = 1; i <= nflines; i++) {
75			printf("This is original line %d\n", i);
76		}
77	}
78	' "$@"
79}
80
81changefile() {
82	$AWK '
83	function random(n) {
84		return int(n * rand())
85	}
86
87	BEGIN {
88		nflines = ARGV[1]
89		chlines = ARGV[2]
90		seed = chlines
91
92		ARGV[1] = ""	# Do not use as filename
93		ARGV[2] = ""	# Do not use as filename
94
95		srand()		# Initialze with current time
96		s = srand()	# get previous seed
97		s = s + seed	# Current time + seed
98		srand(s)	# Better new seed
99
100		for (i = 1; i <= nflines; i++) {
101			change_line[i] = 0
102		}
103
104		while (chlines > 0) {
105			i = random(nflines)
106			if (change_line[i] == 0) {
107				chlines--
108				change_line[i] = 1
109			}
110		}
111		i = 1;
112	}
113
114	{
115		if (change_line[i] != 0) {
116			what = random(3)
117			if (what == 0) {
118				print "Modified, was: " $0
119			} else if (what == 1) {
120				print $0
121				print "New line, inserted after: " $0
122			}
123		} else {
124			print $0
125		}
126		i++
127		next
128	}
129	' "$@"
130}
131
132nlines=$$	# seed startup helper, awk would get seed based on time_t
133maxlines=5000	# Longest file for our tests
134maxch=4		# Max. 25% of all lines are changed
135
136#
137# Diff Program to use. Solaris diff -U0 has bugs, so use our fixed Solaris diff
138# from the SCCS distribution.
139#
140is_bdiff=false
141: ${diff=/opt/schily/ccs/bin/diff}
142type $diff > /dev/null 2> /dev/null
143[ $? -ne 0 ] && diff=diff	# fallback to probably defective system diff
144LC_ALL=C $diff -? 2>&1 | grep -i Option > /dev/null
145if [ $? -ne 0 ]; then
146	LC_ALL=C $diff -? 2>&1 | grep -i "bdiff: arg count" > /dev/null
147	if [ $? -eq 0 ]; then
148		is_bdiff=true
149	else
150		echo "No working diff program found"
151		exit 1
152	fi
153fi
154echo "Using diff programm: $diff"
155#
156# Reference patch program, note that gpatch has problems itself
157# and fails with -diff -C0
158#
159rpatch=gpatch
160LC_ALL=C $rpatch -? 2>&1 | grep -i Option > /dev/null
161if [ $? -ne 0 ]; then
162	echo "Reference patch program \"$rpatch\" not working"
163	rpatch=/usr/bin/patch
164	echo "Trying \"$rpatch\"..."
165	LC_ALL=C $rpatch -? 2>&1 | grep -i Option > /dev/null
166	if [ $? -ne 0 ]; then
167		echo "Reference patch program \"$rpatch\" not working"
168		rpatch=/bin/patch
169		echo "Trying \"$rpatch\"..."
170		LC_ALL=C $rpatch -? 2>&1 | grep -i Option > /dev/null
171		if [ $? -ne 0 ]; then
172			echo "Reference patch program \"$rpatch\" not working"
173			exit 1
174		fi
175	fi
176fi
177echo "Using reference patch programm: $rpatch"
178#
179# Test patch implementation:
180# Add -W+ to permit POSIX + enhancements for "patch -s"
181#
182tpatch="eval ../../OBJ/"`../../../conf/oarch.sh`"/spatch -W+"
183silent=-s
184LC_ALL=C $tpatch -? 2>&1 | grep -i Option > /dev/null
185if [ $? -ne 0 ]; then
186	echo "Test patch program \"$tpatch\" not working"
187	exit 1
188fi
189echo "Using test patch programm: $tpatch"
190
191mod=6
192type ed > /dev/null 2> /dev/null
193[ $? -ne 0 ] && mod=5		# Skip diff -e tests
194if [ $mod = 5 ]; then
195	echo "No ed program found, skipping diff -e tests"
196fi
197
198idx=0
199maxidx=${1:-1000}
200while [ $idx -le $maxidx ]
201do
202	nlines=`rrand 10 $maxlines $nlines`
203
204	max_changes=`expr $nlines / $maxch`
205	changes=`rrand 1 $max_changes $nlines`
206
207	makefile $nlines > original
208	cp original saved_orig
209
210	echo Test $idx:  testing nlines=$nlines changes=$changes
211
212	dtype=`rrand 0 93983 $nlines`	# rrand 0 6 would be of bad quality, so
213	dtype=`expr $dtype \% $mod`	# use "rrand 0 bigprime % 6" instead
214
215	if [ $dtype -eq 0 ]; then
216		dtype="-c"
217	elif [ $dtype -eq 1 ]; then
218		dtype="-u"
219	elif [ $dtype -eq 2 ]; then
220		dtype="-C0"
221	elif [ $dtype -eq 3 ]; then
222		dtype="-U0"
223	elif [ $dtype -eq 5 ]; then
224		dtype="-e"
225	else
226		dtype="  "
227	fi
228	if [ "$is_bdiff" = true ]; then
229		dtype="  "
230	fi
231
232	changefile $nlines $changes original > changed
233	echo diff $dtype original changed
234	echo "Index:original" > patch_file
235	$diff $dtype original changed >> patch_file
236
237	cp saved_orig original
238	#
239	# $rpatch: gpatch is buggy and does not support "diff -C0"
240	# If the reference patch program fails (exit != 0), we use out test patch
241	#
242	$rpatch -D XXX original < patch_file || \
243	( echo "Reference patch program \"$rpatch\" failed, using test patch \"$tpatch\""; \
244	$tpatch $silent -D XXX original < patch_file)
245	#
246	# gpatch never uses "#endif /* XXX */",
247	# FreeBSD patch always adds " /* XXX */", so we need to remove it.
248	# Our patch # depends on wether it is in POSIX mode, where the comment is missing
249	#
250##	sed -e 's^#endif$^#endif /* XXX */^g' < original > expected
251#	cp original expected
252	sed -e 's^#endif /\* XXX \*/$^#endif^g' < original > expected
253
254	if [ "$dtype" = "-e" ]; then
255		diff changed original > xef
256		ret=$?
257		if [ $ret -ne 0 ]; then
258			echo Test $idx: Reference Patch $dtype did not restore original
259			if test `uname`  = FreeBSD; then
260				#
261				# /usr/bin/patch from FreeBSD is buggy.
262				# Pretend it works by copying the right file content.
263				#
264				cp changed expected
265			else
266				[ $ret -eq 1 ] && trap 0
267				exit 1
268			fi
269		fi
270	else
271		$CPP original | grep This > xo
272		$CPP -DXXX original | grep This > xm
273		diff saved_orig xo > xof
274		ret=$?
275		if [ $ret -ne 0 ]; then
276			echo Test $idx: Reference Patch $dtype did not restore original
277			[ $ret -eq 1 ] && trap 0
278			exit 1
279		fi
280		diff changed xm > xmf
281		ret=$?
282		if [ $ret -ne 0 ]; then
283			echo Test $idx: Reference Patch $dtype did not restore changed
284			[ $ret -eq 1 ] && trap 0
285			exit 1
286		fi
287	fi
288
289
290	echo Patching file...
291	cp saved_orig original
292	$tpatch $silent -D XXX original < patch_file
293	ret=$?
294	if [ $ret -ne 0 ]; then
295		echo Test $idx: Patch $dtype returned $ret
296		diff original expected > failure
297		trap 0
298		exit 1
299	fi
300
301	diff original expected > failure
302	ret=$?
303	if [ $ret -ne 0 ]; then
304		echo Test $idx: diff $dtype returned $ret
305		[ $ret -eq 1 ] && trap 0
306		exit 1
307	fi
308	idx=`expr $idx + 1`
309done
310idx=`expr $idx - 1`
311echo Test succeeded after $idx runs...
312