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