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