xref: /freebsd/contrib/netbsd-tests/lib/libc/db/t_db.sh (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
1# $NetBSD: t_db.sh,v 1.7 2016/09/24 20:12:33 christos Exp $
2#
3# Copyright (c) 2008 The NetBSD Foundation, Inc.
4# All rights reserved.
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26#
27
28prog_db()
29{
30	echo $(atf_get_srcdir)/h_db
31}
32
33prog_lfsr()
34{
35	echo $(atf_get_srcdir)/h_lfsr
36}
37
38dict()
39{
40	if [ -f /usr/share/dict/words ]; then
41		echo /usr/share/dict/words
42	elif [ -f /usr/dict/words ]; then
43		echo /usr/dict/words
44	else
45		echo ""
46		atf_fail "no dictionary found"
47	fi
48}
49
50# Begin FreeBSD
51dict()
52{
53	echo /usr/share/dict/words
54}
55# End FreeBSD
56
57SEVEN_SEVEN="abcdefg|abcdefg|abcdefg|abcdefg|abcdefg|abcdefg|abcdefg"
58
59atf_test_case small_btree
60small_btree_head()
61{
62	atf_set "descr" \
63		"Checks btree database using small keys and small data" \
64		"pairs: takes the first hundred entries in the dictionary," \
65		"and makes them be key/data pairs."
66	# Begin FreeBSD
67	atf_set "require.files" /usr/share/dict/words
68	# End FreeBSD
69}
70small_btree_body()
71{
72	TMPDIR="$(pwd)/db_dir"; export TMPDIR
73	mkdir ${TMPDIR}
74
75	sed 200q $(dict) >exp
76
77	for i in `sed 200q $(dict)`; do
78		echo p
79		echo k$i
80		echo d$i
81		echo g
82		echo k$i
83	done >in
84
85	atf_check -o file:exp "$(prog_db)" btree in
86}
87
88atf_test_case small_hash
89small_hash_head()
90{
91	atf_set "descr" \
92		"Checks hash database using small keys and small data" \
93		"pairs: takes the first hundred entries in the dictionary," \
94		"and makes them be key/data pairs."
95	# Begin FreeBSD
96	atf_set "require.files" /usr/share/dict/words
97	# End FreeBSD
98}
99small_hash_body()
100{
101	TMPDIR="$(pwd)/db_dir"; export TMPDIR
102	mkdir ${TMPDIR}
103
104	sed 200q $(dict) >exp
105
106	for i in `sed 200q $(dict)`; do
107		echo p
108		echo k$i
109		echo d$i
110		echo g
111		echo k$i
112	done >in
113
114	atf_check -o file:exp "$(prog_db)" hash in
115}
116
117atf_test_case small_recno
118small_recno_head()
119{
120	atf_set "descr" \
121		"Checks recno database using small keys and small data" \
122		"pairs: takes the first hundred entries in the dictionary," \
123		"and makes them be key/data pairs."
124	# Begin FreeBSD
125	atf_set "require.files" /usr/share/dict/words
126	# End FreeBSD
127}
128small_recno_body()
129{
130	TMPDIR="$(pwd)/db_dir"; export TMPDIR
131	mkdir ${TMPDIR}
132
133	sed 200q $(dict) >exp
134
135	sed 200q $(dict) |
136	awk '{
137		++i;
138		printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i);
139	}' >in
140
141	atf_check -o file:exp "$(prog_db)" recno in
142}
143
144atf_test_case medium_btree
145medium_btree_head()
146{
147	atf_set "descr" \
148		"Checks btree database using small keys and medium" \
149		"data pairs: takes the first 200 entries in the" \
150		"dictionary, and gives them each a medium size data entry."
151	# Begin FreeBSD
152	atf_set "require.files" /usr/share/dict/words
153	# End FreeBSD
154}
155medium_btree_body()
156{
157	TMPDIR="$(pwd)/db_dir"; export TMPDIR
158	mkdir ${TMPDIR}
159
160	mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
161	echo $mdata |
162	awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp
163
164	for i in $(sed 200q $(dict)); do
165		echo p
166		echo k$i
167		echo d$mdata
168		echo g
169		echo k$i
170	done >in
171
172	atf_check -o file:exp "$(prog_db)" btree in
173}
174
175atf_test_case medium_hash
176medium_hash_head()
177{
178	atf_set "descr" \
179		"Checks hash database using small keys and medium" \
180		"data pairs: takes the first 200 entries in the" \
181		"dictionary, and gives them each a medium size data entry."
182	# Begin FreeBSD
183	atf_set "require.files" /usr/share/dict/words
184	# End FreeBSD
185}
186medium_hash_body()
187{
188	TMPDIR="$(pwd)/db_dir"; export TMPDIR
189	mkdir ${TMPDIR}
190
191	mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
192	echo $mdata |
193	awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp
194
195	for i in $(sed 200q $(dict)); do
196		echo p
197		echo k$i
198		echo d$mdata
199		echo g
200		echo k$i
201	done >in
202
203	atf_check -o file:exp "$(prog_db)" hash in
204}
205
206atf_test_case medium_recno
207medium_recno_head()
208{
209	atf_set "descr" \
210		"Checks recno database using small keys and medium" \
211		"data pairs: takes the first 200 entries in the" \
212		"dictionary, and gives them each a medium size data entry."
213}
214medium_recno_body()
215{
216	TMPDIR="$(pwd)/db_dir"; export TMPDIR
217	mkdir ${TMPDIR}
218
219	mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
220	echo $mdata |
221	awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp
222
223	echo $mdata |
224	awk '{  for (i = 1; i < 201; ++i)
225		printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i);
226	}' >in
227
228	atf_check -o file:exp "$(prog_db)" recno in
229}
230
231atf_test_case big_btree
232big_btree_head()
233{
234	atf_set "descr" \
235		"Checks btree database using small keys and big data" \
236		"pairs: inserts the programs in /bin with their paths" \
237		"as their keys."
238}
239big_btree_body()
240{
241	TMPDIR="$(pwd)/db_dir"; export TMPDIR
242	mkdir ${TMPDIR}
243
244	(find /bin -type f -print | xargs cat) >exp
245
246	for psize in 512 16384 65536; do
247		echo "checking page size: $psize"
248
249		for i in `find /bin -type f -print`; do
250			echo p
251			echo k$i
252			echo D$i
253			echo g
254			echo k$i
255		done >in
256
257		atf_check "$(prog_db)" -o out btree in
258		cmp -s exp out || atf_fail "test failed for page size: $psize"
259	done
260}
261
262atf_test_case big_hash
263big_hash_head()
264{
265	atf_set "descr" \
266		"Checks hash database using small keys and big data" \
267		"pairs: inserts the programs in /bin with their paths" \
268		"as their keys."
269}
270big_hash_body()
271{
272	TMPDIR="$(pwd)/db_dir"; export TMPDIR
273	mkdir ${TMPDIR}
274
275	(find /bin -type f -print | xargs cat) >exp
276
277	for i in `find /bin -type f -print`; do
278		echo p
279		echo k$i
280		echo D$i
281		echo g
282		echo k$i
283	done >in
284
285	atf_check "$(prog_db)" -o out hash in
286	cmp -s exp out || atf_fail "test failed"
287}
288
289atf_test_case big_recno
290big_recno_head()
291{
292	atf_set "descr" \
293		"Checks recno database using small keys and big data" \
294		"pairs: inserts the programs in /bin with their paths" \
295		"as their keys."
296}
297big_recno_body()
298{
299	TMPDIR="$(pwd)/db_dir"; export TMPDIR
300	mkdir ${TMPDIR}
301
302	(find /bin -type f -print | xargs cat) >exp
303
304	find /bin -type f -print |
305	awk '{
306		++i;
307		printf("p\nk%d\nD%s\ng\nk%d\n", i, $0, i);
308	}' >in
309
310	for psize in 512 16384 65536; do
311		echo "checking page size: $psize"
312
313		atf_check "$(prog_db)" -o out recno in
314		cmp -s exp out || atf_fail "test failed for page size: $psize"
315	done
316}
317
318atf_test_case random_recno
319random_recno_head()
320{
321	atf_set "descr" "Checks recno database using random entries"
322}
323random_recno_body()
324{
325	TMPDIR="$(pwd)/db_dir"; export TMPDIR
326	mkdir ${TMPDIR}
327
328	echo $SEVEN_SEVEN |
329	awk '{
330		for (i = 37; i <= 37 + 88 * 17; i += 17) {
331			if (i % 41)
332				s = substr($0, 1, i % 41);
333			else
334				s = substr($0, 1);
335			printf("input key %d: %s\n", i, s);
336		}
337		for (i = 1; i <= 15; ++i) {
338			if (i % 41)
339				s = substr($0, 1, i % 41);
340			else
341				s = substr($0, 1);
342			printf("input key %d: %s\n", i, s);
343		}
344		for (i = 19234; i <= 19234 + 61 * 27; i += 27) {
345			if (i % 41)
346				s = substr($0, 1, i % 41);
347			else
348				s = substr($0, 1);
349			printf("input key %d: %s\n", i, s);
350		}
351		exit
352	}' >exp
353
354	cat exp |
355	awk 'BEGIN {
356			i = 37;
357			incr = 17;
358		}
359		{
360			printf("p\nk%d\nd%s\n", i, $0);
361			if (i == 19234 + 61 * 27)
362				exit;
363			if (i == 37 + 88 * 17) {
364				i = 1;
365				incr = 1;
366			} else if (i == 15) {
367				i = 19234;
368				incr = 27;
369			} else
370				i += incr;
371		}
372		END {
373			for (i = 37; i <= 37 + 88 * 17; i += 17)
374				printf("g\nk%d\n", i);
375			for (i = 1; i <= 15; ++i)
376				printf("g\nk%d\n", i);
377			for (i = 19234; i <= 19234 + 61 * 27; i += 27)
378				printf("g\nk%d\n", i);
379		}' >in
380
381	atf_check -o file:exp "$(prog_db)" recno in
382}
383
384atf_test_case reverse_recno
385reverse_recno_head()
386{
387	atf_set "descr" "Checks recno database using reverse order entries"
388}
389reverse_recno_body()
390{
391	TMPDIR="$(pwd)/db_dir"; export TMPDIR
392	mkdir ${TMPDIR}
393
394	echo $SEVEN_SEVEN |
395	awk ' {
396		for (i = 1500; i; --i) {
397			if (i % 34)
398				s = substr($0, 1, i % 34);
399			else
400				s = substr($0, 1);
401			printf("input key %d: %s\n", i, s);
402		}
403		exit;
404	}' >exp
405
406	cat exp |
407	awk 'BEGIN {
408			i = 1500;
409		}
410		{
411			printf("p\nk%d\nd%s\n", i, $0);
412			--i;
413		}
414		END {
415			for (i = 1500; i; --i)
416				printf("g\nk%d\n", i);
417		}' >in
418
419	atf_check -o file:exp "$(prog_db)" recno in
420}
421
422atf_test_case alternate_recno
423alternate_recno_head()
424{
425	atf_set "descr" "Checks recno database using alternating order entries"
426}
427alternate_recno_body()
428{
429	TMPDIR="$(pwd)/db_dir"; export TMPDIR
430	mkdir ${TMPDIR}
431
432	echo $SEVEN_SEVEN |
433	awk ' {
434		for (i = 1; i < 1200; i += 2) {
435			if (i % 34)
436				s = substr($0, 1, i % 34);
437			else
438				s = substr($0, 1);
439			printf("input key %d: %s\n", i, s);
440		}
441		for (i = 2; i < 1200; i += 2) {
442			if (i % 34)
443				s = substr($0, 1, i % 34);
444			else
445				s = substr($0, 1);
446			printf("input key %d: %s\n", i, s);
447		}
448		exit;
449	}' >exp
450
451	cat exp |
452	awk 'BEGIN {
453			i = 1;
454			even = 0;
455		}
456		{
457			printf("p\nk%d\nd%s\n", i, $0);
458			i += 2;
459			if (i >= 1200) {
460				if (even == 1)
461					exit;
462				even = 1;
463				i = 2;
464			}
465		}
466		END {
467			for (i = 1; i < 1200; ++i)
468				printf("g\nk%d\n", i);
469		}' >in
470
471	atf_check "$(prog_db)" -o out recno in
472
473	sort -o exp exp
474	sort -o out out
475
476	cmp -s exp out || atf_fail "test failed"
477}
478
479h_delete()
480{
481	TMPDIR="$(pwd)/db_dir"; export TMPDIR
482	mkdir ${TMPDIR}
483
484	type=$1
485
486	echo $SEVEN_SEVEN |
487	awk '{
488		for (i = 1; i <= 120; ++i)
489			printf("%05d: input key %d: %s\n", i, i, $0);
490	}' >exp
491
492	cat exp |
493	awk '{
494		printf("p\nk%d\nd%s\n", ++i, $0);
495	}
496	END {
497		printf("fR_NEXT\n");
498		for (i = 1; i <= 120; ++i)
499			printf("s\n");
500		printf("fR_CURSOR\ns\nkXX\n");
501		printf("r\n");
502		printf("fR_NEXT\ns\n");
503		printf("fR_CURSOR\ns\nk1\n");
504		printf("r\n");
505		printf("fR_FIRST\ns\n");
506	}' >in
507
508	# For btree, the records are ordered by the string representation
509	# of the key value.  So sort the expected output file accordingly,
510	# and set the seek_last key to the last expected key value.
511
512	if [ "$type" = "btree" ] ; then
513		sed -e 's/kXX/k99/' < in > tmp
514		mv tmp in
515		sort -d -k4 < exp > tmp
516		mv tmp exp
517		echo $SEVEN_SEVEN |
518		awk '{
519			printf("%05d: input key %d: %s\n", 99, 99, $0);
520			printf("seq failed, no such key\n");
521			printf("%05d: input key %d: %s\n", 1, 1, $0);
522			printf("%05d: input key %d: %s\n", 10, 10, $0);
523			exit;
524		}' >> exp
525	else
526	# For recno, records are ordered by numerical key value.  No sort
527	# is needed, but still need to set proper seek_last key value.
528		sed -e 's/kXX/k120/' < in > tmp
529		mv tmp in
530		echo $SEVEN_SEVEN |
531		awk '{
532			printf("%05d: input key %d: %s\n", 120, 120, $0);
533			printf("seq failed, no such key\n");
534			printf("%05d: input key %d: %s\n", 1, 1, $0);
535			printf("%05d: input key %d: %s\n", 2, 2, $0);
536			exit;
537		}' >> exp
538	fi
539
540	atf_check "$(prog_db)" -o out $type in
541	atf_check -o file:exp cat out
542}
543
544atf_test_case delete_btree
545delete_btree_head()
546{
547	atf_set "descr" "Checks removing records in btree database"
548}
549delete_btree_body()
550{
551	h_delete btree
552}
553
554atf_test_case delete_recno
555delete_recno_head()
556{
557	atf_set "descr" "Checks removing records in recno database"
558}
559delete_recno_body()
560{
561	h_delete recno
562}
563
564h_repeated()
565{
566	local type="$1"
567	TMPDIR="$(pwd)/db_dir"; export TMPDIR
568	mkdir ${TMPDIR}
569
570	echo "" |
571	awk 'BEGIN {
572		for (i = 1; i <= 10; ++i) {
573			printf("p\nkkey1\nD/bin/sh\n");
574			printf("p\nkkey2\nD/bin/csh\n");
575			if (i % 8 == 0) {
576				printf("c\nkkey2\nD/bin/csh\n");
577				printf("c\nkkey1\nD/bin/sh\n");
578				printf("e\t%d of 10 (comparison)\n", i);
579			} else
580				printf("e\t%d of 10             \n", i);
581			printf("r\nkkey1\nr\nkkey2\n");
582		}
583	}' >in
584
585	$(prog_db) $type in
586}
587
588atf_test_case repeated_btree
589repeated_btree_head()
590{
591	atf_set "descr" \
592		"Checks btree database with repeated small keys and" \
593		"big data pairs. Makes sure that overflow pages are reused"
594}
595repeated_btree_body()
596{
597	h_repeated btree
598}
599
600atf_test_case repeated_hash
601repeated_hash_head()
602{
603	atf_set "descr" \
604		"Checks hash database with repeated small keys and" \
605		"big data pairs. Makes sure that overflow pages are reused"
606}
607repeated_hash_body()
608{
609	h_repeated hash
610}
611
612atf_test_case duplicate_btree
613duplicate_btree_head()
614{
615	atf_set "descr" "Checks btree database with duplicate keys"
616}
617duplicate_btree_body()
618{
619	TMPDIR="$(pwd)/db_dir"; export TMPDIR
620	mkdir ${TMPDIR}
621
622	echo $SEVEN_SEVEN |
623	awk '{
624		for (i = 1; i <= 543; ++i)
625			printf("%05d: input key %d: %s\n", i, i, $0);
626		exit;
627	}' >exp
628
629	cat exp |
630	awk '{
631		if (i++ % 2)
632			printf("p\nkduplicatekey\nd%s\n", $0);
633		else
634			printf("p\nkunique%dkey\nd%s\n", i, $0);
635	}
636	END {
637			printf("o\n");
638	}' >in
639
640	atf_check -o file:exp -x "$(prog_db) -iflags=1 btree in | sort"
641}
642
643h_cursor_flags()
644{
645	local type=$1
646	TMPDIR="$(pwd)/db_dir"; export TMPDIR
647	mkdir ${TMPDIR}
648
649	echo $SEVEN_SEVEN |
650	awk '{
651		for (i = 1; i <= 20; ++i)
652			printf("%05d: input key %d: %s\n", i, i, $0);
653		exit;
654	}' >exp
655
656	# Test that R_CURSOR doesn't succeed before cursor initialized
657	cat exp |
658	awk '{
659		if (i == 10)
660			exit;
661		printf("p\nk%d\nd%s\n", ++i, $0);
662	}
663	END {
664		printf("fR_CURSOR\nr\n");
665		printf("eR_CURSOR SHOULD HAVE FAILED\n");
666	}' >in
667
668	atf_check -o ignore -e ignore -s ne:0 "$(prog_db)" -o out $type in
669	atf_check -s ne:0 test -s out
670
671	cat exp |
672	awk '{
673		if (i == 10)
674			exit;
675		printf("p\nk%d\nd%s\n", ++i, $0);
676	}
677	END {
678		printf("fR_CURSOR\np\nk1\ndsome data\n");
679		printf("eR_CURSOR SHOULD HAVE FAILED\n");
680	}' >in
681
682	atf_check -o ignore -e ignore -s ne:0 "$(prog_db)" -o out $type in
683	atf_check -s ne:0 test -s out
684}
685
686atf_test_case cursor_flags_btree
687cursor_flags_btree_head()
688{
689	atf_set "descr" \
690		"Checks use of cursor flags without initialization in btree database"
691}
692cursor_flags_btree_body()
693{
694	h_cursor_flags btree
695}
696
697atf_test_case cursor_flags_recno
698cursor_flags_recno_head()
699{
700	atf_set "descr" \
701		"Checks use of cursor flags without initialization in recno database"
702}
703cursor_flags_recno_body()
704{
705	h_cursor_flags recno
706}
707
708atf_test_case reverse_order_recno
709reverse_order_recno_head()
710{
711	atf_set "descr" "Checks reverse order inserts in recno database"
712}
713reverse_order_recno_body()
714{
715	TMPDIR="$(pwd)/db_dir"; export TMPDIR
716	mkdir ${TMPDIR}
717
718	echo $SEVEN_SEVEN |
719	awk '{
720		for (i = 1; i <= 779; ++i)
721			printf("%05d: input key %d: %s\n", i, i, $0);
722		exit;
723	}' >exp
724
725	cat exp |
726	awk '{
727		if (i == 0) {
728			i = 1;
729			printf("p\nk1\nd%s\n", $0);
730			printf("%s\n", "fR_IBEFORE");
731		} else
732			printf("p\nk1\nd%s\n", $0);
733	}
734	END {
735			printf("or\n");
736	}' >in
737
738	atf_check -o file:exp "$(prog_db)" recno in
739}
740
741atf_test_case small_page_btree
742small_page_btree_head()
743{
744	atf_set "descr" \
745		"Checks btree database with lots of keys and small page" \
746		"size: takes the first 20000 entries in the dictionary," \
747		"reverses them, and gives them each a small size data" \
748		"entry. Uses a small page size to make sure the btree" \
749		"split code gets hammered."
750	# Begin FreeBSD
751	atf_set "require.files" /usr/share/dict/words
752	# End FreeBSD
753}
754small_page_btree_body()
755{
756	TMPDIR="$(pwd)/db_dir"; export TMPDIR
757	mkdir ${TMPDIR}
758
759	mdata=abcdefghijklmnopqrstuvwxy
760	echo $mdata |
761	awk '{ for (i = 1; i < 20001; ++i) print $0 }' >exp
762
763	for i in `sed 20000q $(dict) | rev`; do
764		echo p
765		echo k$i
766		echo d$mdata
767		echo g
768		echo k$i
769	done >in
770
771	atf_check -o file:exp "$(prog_db)" -i psize=512 btree in
772}
773
774h_byte_orders()
775{
776	TMPDIR="$(pwd)/db_dir"; export TMPDIR
777	mkdir ${TMPDIR}
778
779	type=$1
780
781	sed 50q $(dict) >exp
782	for order in 1234 4321; do
783		for i in `sed 50q $(dict)`; do
784			echo p
785			echo k$i
786			echo d$i
787			echo S
788			echo g
789			echo k$i
790		done >in
791
792		atf_check -o file:exp "$(prog_db)" -ilorder=$order -f byte.file $type in
793
794		for i in `sed 50q $(dict)`; do
795			echo g
796			echo k$i
797		done >in
798
799		atf_check -o file:exp "$(prog_db)" -s -ilorder=$order -f byte.file $type in
800	done
801}
802
803atf_test_case byte_orders_btree
804byte_orders_btree_head()
805{
806	atf_set "descr" "Checks btree database using differing byte orders"
807	# Begin FreeBSD
808	atf_set "require.files" /usr/share/dict/words
809	# End FreeBSD
810}
811byte_orders_btree_body()
812{
813	h_byte_orders btree
814}
815
816atf_test_case byte_orders_hash
817byte_orders_hash_head()
818{
819	atf_set "descr" "Checks hash database using differing byte orders"
820}
821byte_orders_hash_body()
822{
823	h_byte_orders hash
824}
825
826h_bsize_ffactor()
827{
828	bsize=$1
829	ffactor=$2
830
831	echo "bucketsize $bsize, fill factor $ffactor"
832	atf_check -o file:exp "$(prog_db)" "-ibsize=$bsize,\
833ffactor=$ffactor,nelem=25000,cachesize=65536" hash in
834}
835
836atf_test_case bsize_ffactor
837bsize_ffactor_head()
838{
839	atf_set "timeout" "1800"
840	atf_set "descr" "Checks hash database with various" \
841					"bucketsizes and fill factors"
842	# Begin FreeBSD
843	atf_set "require.files" /usr/share/dict/words
844	# End FreeBSD
845}
846bsize_ffactor_body()
847{
848	TMPDIR="$(pwd)/db_dir"; export TMPDIR
849	mkdir ${TMPDIR}
850
851	echo $SEVEN_SEVEN |
852	awk '{
853		for (i = 1; i <= 10000; ++i) {
854			if (i % 34)
855				s = substr($0, 1, i % 34);
856			else
857				s = substr($0, 1);
858			printf("%s\n", s);
859		}
860		exit;
861
862	}' >exp
863
864	sed 10000q $(dict) |
865	awk 'BEGIN {
866		ds="'$SEVEN_SEVEN'"
867	}
868	{
869		if (++i % 34)
870			s = substr(ds, 1, i % 34);
871		else
872			s = substr(ds, 1);
873		printf("p\nk%s\nd%s\n", $0, s);
874	}' >in
875
876	sed 10000q $(dict) |
877	awk '{
878		++i;
879		printf("g\nk%s\n", $0);
880	}' >>in
881
882	h_bsize_ffactor 256 11
883	h_bsize_ffactor 256 14
884	h_bsize_ffactor 256 21
885
886	h_bsize_ffactor 512 21
887	h_bsize_ffactor 512 28
888	h_bsize_ffactor 512 43
889
890	h_bsize_ffactor 1024 43
891	h_bsize_ffactor 1024 57
892	h_bsize_ffactor 1024 85
893
894	h_bsize_ffactor 2048 85
895	h_bsize_ffactor 2048 114
896	h_bsize_ffactor 2048 171
897
898	h_bsize_ffactor 4096 171
899	h_bsize_ffactor 4096 228
900	h_bsize_ffactor 4096 341
901
902	h_bsize_ffactor 8192 341
903	h_bsize_ffactor 8192 455
904	h_bsize_ffactor 8192 683
905
906	h_bsize_ffactor 16384 341
907	h_bsize_ffactor 16384 455
908	h_bsize_ffactor 16384 683
909
910	h_bsize_ffactor 32768 341
911	h_bsize_ffactor 32768 455
912	h_bsize_ffactor 32768 683
913
914	# Begin FreeBSD
915	if false; then
916	# End FreeBSD
917	h_bsize_ffactor 65536 341
918	h_bsize_ffactor 65536 455
919	h_bsize_ffactor 65536 683
920	# Begin FreeBSD
921	fi
922	# End FreeBSD
923}
924
925# This tests 64K block size addition/removal
926atf_test_case four_char_hash
927four_char_hash_head()
928{
929	atf_set "descr" \
930		"Checks hash database with 4 char key and" \
931		"value insert on a 65536 bucket size"
932}
933four_char_hash_body()
934{
935	TMPDIR="$(pwd)/db_dir"; export TMPDIR
936	mkdir ${TMPDIR}
937
938	cat >in <<EOF
939p
940k1234
941d1234
942r
943k1234
944EOF
945
946	# Begin FreeBSD
947	if true; then
948		atf_check "$(prog_db)" -i bsize=32768 hash in
949	else
950	# End FreeBSD
951	atf_check "$(prog_db)" -i bsize=65536 hash in
952	# Begin FreeBSD
953	fi
954	# End FreeBSD
955}
956
957
958atf_test_case bsize_torture
959bsize_torture_head()
960{
961	atf_set "timeout" "36000"
962	atf_set "descr" "Checks hash database with various bucket sizes"
963}
964bsize_torture_body()
965{
966	TMPDIR="$(pwd)/db_dir"; export TMPDIR
967	mkdir ${TMPDIR}
968	# Begin FreeBSD
969	#
970	# db(3) doesn't support 64kB bucket sizes
971	for i in 2048 4096 8192 16384 32768 # 65536
972	# End FreeBSD
973	do
974		atf_check "$(prog_lfsr)" $i
975	done
976}
977
978atf_test_case btree_weird_page_split
979btree_weird_page_split_head()
980{
981	atf_set "descr"  \
982	    "Test for a weird page split condition where an insertion " \
983	    "into index 0 of a page that would cause the new item to " \
984	    "be the only item on the left page results in index 0 of " \
985	    "the right page being erroneously skipped; this only " \
986	    "happens with one particular key+data length for each page size."
987}
988btree_weird_page_split_body()
989{
990	for psize in 512 1024 2048 4096 8192; do
991		echo "    page size $psize"
992		kdsizes=`awk 'BEGIN {
993			psize = '$psize'; hsize = int(psize/2);
994			for (kdsize = hsize-40; kdsize <= hsize; kdsize++) {
995				print kdsize;
996			}
997		}' /dev/null`
998
999		# Use a series of keylen+datalen values in the right
1000		# neighborhood to find the one that triggers the bug.
1001		# We could compute the exact size that triggers the
1002		# bug but this additional fuzz may be useful.
1003
1004		# Insert keys in reverse order to maximize the chances
1005		# for a split on index 0.
1006
1007		for kdsize in $kdsizes; do
1008			awk 'BEGIN {
1009				kdsize = '$kdsize';
1010				for (i = 8; i-- > 0; ) {
1011					s = sprintf("a%03d:%09d", i, kdsize);
1012					for (j = 0; j < kdsize-20; j++) {
1013						s = s "x";
1014					}
1015					printf("p\nka%03d\nd%s\n", i, s);
1016				}
1017				print "o";
1018			}' /dev/null > in
1019			sed -n 's/^d//p' in | sort > exp
1020			atf_check -o file:exp \
1021			    "$(prog_db)" -i psize=$psize btree in
1022		done
1023	done
1024}
1025
1026# Extremely tricky test attempting to replicate some unusual database
1027# corruption seen in the field: pieces of the database becoming
1028# inaccessible to random access, sequential access, or both.  The
1029# hypothesis is that at least some of these are triggered by the bug
1030# in page splits on index 0 with a particular exact keylen+datalen.
1031# (See Test 40.)  For psize=4096, this size is exactly 2024.
1032
1033# The order of operations here relies on very specific knowledge of
1034# the internals of the btree access method in order to place records
1035# at specific offsets in a page and to create certain keys on internal
1036# pages.  The to-be-split page immediately prior to the bug-triggering
1037# split has the following properties:
1038#
1039# * is not the leftmost leaf page
1040# * key on the parent page is compares less than the key of the item
1041#   on index 0
1042# * triggering record's key also compares greater than the key on the
1043#   parent page
1044
1045# Additionally, we prime the mpool LRU chain so that the head page on
1046# the chain has the following properties:
1047#
1048# * record at index 0 is located where it will not get overwritten by
1049#   items written to the right-hand page during the split
1050# * key of the record at index 0 compares less than the key of the
1051#   bug-triggering record
1052
1053# If the page-split bug exists, this test appears to create a database
1054# where some records are inaccessible to a search, but still remain in
1055# the file and are accessible by sequential traversal.  At least one
1056# record gets duplicated out of sequence.
1057
1058atf_test_case btree_tricky_page_split
1059btree_tricky_page_split_head()
1060{
1061	atf_set "descr"  \
1062	    "btree: no unsearchables due to page split on index 0"
1063}
1064btree_tricky_page_split_body()
1065{
1066	list=`(for i in a b c d; do
1067			for j in 990 998 999; do
1068				echo g ${i}${j} 1024
1069			done
1070		done;
1071		echo g y997 2014
1072		for i in y z; do
1073			for j in 998 999; do
1074				echo g ${i}${j} 1024
1075			done
1076		done)`
1077	# Exact number for trigger condition accounts for newlines
1078	# retained by dbtest with -ofile but not without; we use
1079	# -ofile, so count newlines.  keylen=5,datalen=5+2014 for
1080	# psize=4096 here.
1081	(cat - <<EOF
1082p z999 1024
1083p z998 1024
1084p y999 1024
1085p y990 1024
1086p d999 1024
1087p d990 1024
1088p c999 1024
1089p c990 1024
1090p b999 1024
1091p b990 1024
1092p a999 1024
1093p a990 1024
1094p y998 1024
1095r y990
1096p d998 1024
1097p d990 1024
1098p c998 1024
1099p c990 1024
1100p b998 1024
1101p b990 1024
1102p a998 1024
1103p a990 1024
1104p y997 2014
1105S
1106o
1107EOF
1108	echo "$list") |
1109	# awk script input:
1110	# {p|g|r} key [datasize]
1111	awk '/^[pgr]/{
1112		printf("%s\nk%s\n", $1, $2);
1113	}
1114	/^p/{
1115		s = $2;
1116		for (i = 0; i < $3; i++) {
1117			s = s "x";
1118		}
1119		printf("d%s\n", s);
1120	}
1121	!/^[pgr]/{
1122		print $0;
1123	}' > in
1124	(echo "$list"; echo "$list") | awk '{
1125		s = $2;
1126		for (i = 0; i < $3; i++) {
1127			s = s "x";
1128		}
1129		print s;
1130	}' > exp
1131	atf_check -o file:exp \
1132	    "$(prog_db)" -i psize=4096 btree in
1133}
1134
1135# Begin FreeBSD
1136if false; then
1137# End FreeBSD
1138atf_test_case btree_recursive_traversal
1139btree_recursive_traversal_head()
1140{
1141	atf_set "descr"  \
1142	    "btree: Test for recursive traversal successfully " \
1143	    "retrieving records that are inaccessible to normal " \
1144	    "sequential 'sibling-link' traversal. This works by " \
1145	    "unlinking a few leaf pages but leaving their parent " \
1146	    "links intact. To verify that the unlink actually makes " \
1147	    "records inaccessible, the test first uses 'o' to do a " \
1148	    "normal sequential traversal, followed by 'O' to do a " \
1149	    "recursive traversal."
1150}
1151btree_recursive_traversal_body()
1152{
1153	fill="abcdefghijklmnopqrstuvwxyzy"
1154	script='{
1155		for (i = 0; i < 20000; i++) {
1156			printf("p\nkAA%05d\nd%05d%s\n", i, i, $0);
1157		}
1158		print "u";
1159		print "u";
1160		print "u";
1161		print "u";
1162	}'
1163	(echo $fill | awk "$script"; echo o) > in1
1164	echo $fill |
1165	awk '{
1166		for (i = 0; i < 20000; i++) {
1167			if (i >= 5 && i <= 40)
1168				continue;
1169			printf("%05d%s\n", i, $0);
1170		}
1171	}' > exp1
1172	atf_check -o file:exp1 \
1173	    "$(prog_db)" -i psize=512 btree in1
1174	echo $fill |
1175	awk '{
1176		for (i = 0; i < 20000; i++) {
1177			printf("%05d%s\n", i, $0);
1178		}
1179	}' > exp2
1180	(echo $fill | awk "$script"; echo O) > in2
1181	atf_check -o file:exp2 \
1182	    "$(prog_db)" -i psize=512 btree in2
1183}
1184# Begin FreeBSD
1185fi
1186# End FreeBSD
1187
1188atf_test_case btree_byteswap_unaligned_access_bksd
1189btree_byteswap_unaligned_access_bksd_head()
1190{
1191	atf_set "descr"  \
1192	    "btree: big key, small data, byteswap unaligned access"
1193}
1194btree_byteswap_unaligned_access_bksd_body()
1195{
1196	(echo foo; echo bar) |
1197	awk '{
1198		s = $0
1199		for (i = 0; i < 488; i++) {
1200			s = s "x";
1201		}
1202		printf("p\nk%s\ndx\n", s);
1203	}' > in
1204	for order in 1234 4321; do
1205		atf_check \
1206		    "$(prog_db)" -o out -i psize=512,lorder=$order btree in
1207	done
1208}
1209
1210atf_test_case btree_byteswap_unaligned_access_skbd
1211btree_byteswap_unaligned_access_skbd_head()
1212{
1213	atf_set "descr"  \
1214	    "btree: small key, big data, byteswap unaligned access"
1215}
1216btree_byteswap_unaligned_access_skbd_body()
1217{
1218	# 484 = 512 - 20 (header) - 7 ("foo1234") - 1 (newline)
1219	(echo foo1234; echo bar1234) |
1220	awk '{
1221		s = $0
1222		for (i = 0; i < 484; i++) {
1223			s = s "x";
1224		}
1225		printf("p\nk%s\nd%s\n", $0, s);
1226	}' > in
1227	for order in 1234 4321; do
1228		atf_check \
1229		    "$(prog_db)" -o out -i psize=512,lorder=$order btree in
1230	done
1231}
1232
1233atf_test_case btree_known_byte_order
1234btree_known_byte_order_head()
1235{
1236	atf_set "descr"  \
1237	    "btree: small key, big data, known byte order"
1238}
1239btree_known_byte_order_body()
1240{
1241	local a="-i psize=512,lorder="
1242
1243	(echo foo1234; echo bar1234) |
1244	awk '{
1245		s = $0
1246		for (i = 0; i < 484; i++) {
1247			s = s "x";
1248		}
1249		printf("%s\n", s);
1250	}' > exp
1251	(echo foo1234; echo bar1234) |
1252	awk '{
1253		s = $0
1254		for (i = 0; i < 484; i++) {
1255			s = s "x";
1256		}
1257		printf("p\nk%s\nd%s\n", $0, s);
1258	}' > in1
1259	for order in 1234 4321; do
1260		atf_check \
1261		    "$(prog_db)" -f out.$order $a$order btree in1
1262	done
1263	(echo g; echo kfoo1234; echo g; echo kbar1234) > in2
1264	for order in 1234 4321; do
1265		atf_check -o file:exp \
1266		    "$(prog_db)" -s -f out.$order $a$order btree in2
1267	done
1268}
1269
1270atf_init_test_cases()
1271{
1272	atf_add_test_case small_btree
1273	atf_add_test_case small_hash
1274	atf_add_test_case small_recno
1275	atf_add_test_case medium_btree
1276	atf_add_test_case medium_hash
1277	atf_add_test_case medium_recno
1278	atf_add_test_case big_btree
1279	atf_add_test_case big_hash
1280	atf_add_test_case big_recno
1281	atf_add_test_case random_recno
1282	atf_add_test_case reverse_recno
1283	atf_add_test_case alternate_recno
1284	atf_add_test_case delete_btree
1285	atf_add_test_case delete_recno
1286	atf_add_test_case repeated_btree
1287	atf_add_test_case repeated_hash
1288	atf_add_test_case duplicate_btree
1289	atf_add_test_case cursor_flags_btree
1290	atf_add_test_case cursor_flags_recno
1291	atf_add_test_case reverse_order_recno
1292	atf_add_test_case small_page_btree
1293	atf_add_test_case byte_orders_btree
1294	atf_add_test_case byte_orders_hash
1295	atf_add_test_case bsize_ffactor
1296	atf_add_test_case four_char_hash
1297	atf_add_test_case bsize_torture
1298	atf_add_test_case btree_weird_page_split
1299	atf_add_test_case btree_tricky_page_split
1300	# Begin FreeBSD
1301	if false; then
1302	# End FreeBSD
1303	atf_add_test_case btree_recursive_traversal
1304	# Begin FreeBSD
1305	fi
1306	# End FreeBSD
1307	atf_add_test_case btree_byteswap_unaligned_access_bksd
1308	atf_add_test_case btree_byteswap_unaligned_access_skbd
1309	atf_add_test_case btree_known_byte_order
1310}
1311