📄 btree2.test
字号:
# 2001 September 15## The author disclaims copyright to this source code. In place of# a legal notice, here is a blessing:## May you do good and not evil.# May you find forgiveness for yourself and forgive others.# May you share freely, never taking more than you give.##***********************************************************************# This file implements regression tests for SQLite library. The# focus of this script is btree database backend## $Id: btree2.test,v 1.10 2002/02/19 13:39:23 drh Exp $set testdir [file dirname $argv0]source $testdir/tester.tclif {[info commands btree_open]!=""} {# Create a new database file containing no entries. The database should# contain 5 tables:## 2 The descriptor table# 3 The foreground table# 4 The background table# 5 The long key table# 6 The long data table## An explanation for what all these tables are used for is provided below.#do_test btree2-1.1 { expr srand(1) file delete -force test2.bt file delete -force test2.bt-journal set ::b [btree_open test2.bt] btree_begin_transaction $::b btree_create_table $::b} {3}do_test btree2-1.2 { btree_create_table $::b} {4}do_test btree2-1.3 { btree_create_table $::b} {5}do_test btree2-1.4 { btree_create_table $::b} {6}do_test btree2-1.5 { set ::c2 [btree_cursor $::b 2 1] btree_insert $::c2 {one} {1} btree_delete $::c2 btree_close_cursor $::c2 btree_commit $::b btree_integrity_check $::b 2 3 4 5 6} {}# This test module works by making lots of pseudo-random changes to a# database while simultaneously maintaining an invariant on that database.# Periodically, the script does a sanity check on the database and verifies# that the invariant is satisfied.## The invariant is as follows:## 1. The descriptor table always contains 2 enters. An entry keyed by# "N" is the number of elements in the foreground and background tables# combined. The entry keyed by "L" is the number of digits in the keys# for foreground and background tables.## 2. The union of the foreground an background tables consists of N entries# where each entry an L-digit key. (Actually, some keys can be longer # than L characters, but they always start with L digits.) The keys# cover all integers between 1 and N. Whenever an entry is added to# the foreground it is removed form the background and vice versa.## 3. Some entries in the foreground and background tables have keys that# begin with an L-digit number but are followed by additional characters.# For each such entry there is a corresponding entry in the long key# table. The long key table entry has a key which is just the L-digit# number and data which is the length of the key in the foreground and# background tables.## 4. The data for both foreground and background entries is usually a# short string. But some entries have long data strings. For each# such entries there is an entry in the long data type. The key to# long data table is an L-digit number. (The extension on long keys# is omitted.) The data is the number of charaters in the data of the# foreground or background entry.## The following function builds a database that satisfies all of the above# invariants.#proc build_db {N L} { for {set i 2} {$i<=6} {incr i} { catch {btree_close_cursor [set ::c$i]} btree_clear_table $::b $i set ::c$i [btree_cursor $::b $i 1] } btree_insert $::c2 N $N btree_insert $::c2 L $L set format %0${L}d for {set i 1} {$i<=$N} {incr i} { set key [format $format $i] set data $key btree_insert $::c3 $key $data }}# Given a base key number and a length, construct the full text of the key# or data.#proc make_payload {keynum L len} { set key [format %0${L}d $keynum] set r $key set i 1 while {[string length $r]<$len} { append r " ($i) $key" incr i } return [string range $r 0 [expr {$len-1}]]}# Verify the invariants on the database. Return an empty string on # success or an error message if something is amiss.#proc check_invariants {} { set ck [btree_integrity_check $::b 2 3 4 5 6] if {$ck!=""} { puts "\n*** SANITY:\n$ck" exit return $ck } btree_move_to $::c3 {} btree_move_to $::c4 {} btree_move_to $::c2 N set N [btree_data $::c2] btree_move_to $::c2 L set L [btree_data $::c2] set LM1 [expr {$L-1}] for {set i 1} {$i<=$N} {incr i} { set key [btree_key $::c3] if {[scan $key %d k]<1} {set k 0} if {$k!=$i} { set key [btree_key $::c4] if {[scan $key %d k]<1} {set k 0} if {$k!=$i} { # puts "MISSING $i" # puts {Page 3:}; btree_page_dump $::b 3 # puts {Page 4:}; btree_page_dump $::b 4 # exit return "Key $i is missing from both foreground and background" } set data [btree_data $::c4] btree_next $::c4 } else { set data [btree_data $::c3] btree_next $::c3 } set skey [string range $key 0 $LM1] if {[btree_move_to $::c5 $skey]==0} { set keylen [btree_data $::c5] } else { set keylen $L } if {[string length $key]!=$keylen} { return "Key $i is the wrong size.\ Is \"$key\" but should be \"[make_payload $k $L $keylen]\"" } if {[make_payload $k $L $keylen]!=$key} { return "Key $i has an invalid extension" } if {[btree_move_to $::c6 $skey]==0} { set datalen [btree_data $::c6] } else { set datalen $L } if {[string length $data]!=$datalen} { return "Data for $i is the wrong size.\ Is [string length $data] but should be $datalen" } if {[make_payload $k $L $datalen]!=$data} { return "Entry $i has an incorrect data" } }}# Make random changes to the database such that each change preserves# the invariants. The number of changes is $n*N where N is the parameter# from the descriptor table. Each changes begins with a random key.# the entry with that key is put in the foreground table with probability# $I and it is put in background with probability (1.0-$I). It gets# a long key with probability $K and long data with probability $D. # set chngcnt 0proc random_changes {n I K D} { btree_move_to $::c2 N set N [btree_data $::c2] btree_move_to $::c2 L set L [btree_data $::c2] set LM1 [expr {$L-1}] set total [expr {int($N*$n)}] set format %0${L}d for {set i 0} {$i<$total} {incr i} { set k [expr {int(rand()*$N)+1}] set insert [expr {rand()<=$I}] set longkey [expr {rand()<=$K}] set longdata [expr {rand()<=$D}] # incr ::chngcnt # if {$::chngcnt==251} {btree_tree_dump $::b 3} # puts "CHANGE $::chngcnt: $k $insert $longkey $longdata" if {$longkey} { set x [expr {rand()}] set keylen [expr {int($x*$x*$x*$x*3000)+10}] } else { set keylen $L } set key [make_payload $k $L $keylen] if {$longdata} { set x [expr {rand()}] set datalen [expr {int($x*$x*$x*$x*3000)+10}] } else { set datalen $L } set data [make_payload $k $L $datalen] set basekey [format $format $k] if {[set c [btree_move_to $::c3 $basekey]]==0} { btree_delete $::c3 } else { if {$c<0} {btree_next $::c3} if {[string match $basekey* [btree_key $::c3]]} { btree_delete $::c3 } } if {[set c [btree_move_to $::c4 $basekey]]==0} { btree_delete $::c4 } else { if {$c<0} {btree_next $::c4} if {[string match $basekey* [btree_key $::c4]]} { btree_delete $::c4 } } if {[scan [btree_key $::c4] %d kx]<1} {set kx -1} if {$kx==$k} { btree_delete $::c4 } if {$insert} { btree_insert $::c3 $key $data } else { btree_insert $::c4 $key $data } if {$longkey} { btree_insert $::c5 $basekey $keylen } elseif {[btree_move_to $::c5 $basekey]==0} { btree_delete $::c5 } if {$longdata} { btree_insert $::c6 $basekey $datalen } elseif {[btree_move_to $::c6 $basekey]==0} { btree_delete $::c6 } # set ck [btree_integrity_check $::b 2 3 4 5 6] # if {$ck!=""} { # puts "\nSANITY CHECK FAILED!\n$ck" # exit # } # puts "PAGE 3:"; btree_page_dump $::b 3 # puts "PAGE 4:"; btree_page_dump $::b 4 }}# Repeat this test sequence on database of various sizes#set testno 2foreach {N L} { 10 2 50 2 200 3 2000 5} { puts "**** N=$N L=$L ****" set hash [md5file test2.bt] do_test btree2-$testno.1 [subst -nocommands { set ::c2 [btree_cursor $::b 2 1] set ::c3 [btree_cursor $::b 3 1] set ::c4 [btree_cursor $::b 4 1] set ::c5 [btree_cursor $::b 5 1] set ::c6 [btree_cursor $::b 6 1] btree_begin_transaction $::b build_db $N $L check_invariants }] {} do_test btree2-$testno.2 { btree_close_cursor $::c2 btree_close_cursor $::c3 btree_close_cursor $::c4 btree_close_cursor $::c5 btree_close_cursor $::c6 btree_rollback $::b md5file test2.bt } $hash do_test btree2-$testno.3 [subst -nocommands { btree_begin_transaction $::b set ::c2 [btree_cursor $::b 2 1] set ::c3 [btree_cursor $::b 3 1] set ::c4 [btree_cursor $::b 4 1] set ::c5 [btree_cursor $::b 5 1] set ::c6 [btree_cursor $::b 6 1] build_db $N $L check_invariants }] {} do_test btree2-$testno.4 { btree_commit $::b check_invariants } {} do_test btree2-$testno.5 { lindex [btree_pager_stats $::b] 1 } {6} do_test btree2-$testno.6 { btree_close_cursor $::c2 btree_close_cursor $::c3 btree_close_cursor $::c4 btree_close_cursor $::c5 btree_close_cursor $::c6 lindex [btree_pager_stats $::b] 1 } {0} do_test btree2-$testno.7 { btree_close $::b } {}after 100 # For each database size, run various changes tests. # set num2 1 foreach {n I K D} { 0.5 0.5 0.1 0.1 1.0 0.2 0.1 0.1 1.0 0.8 0.1 0.1 2.0 0.0 0.1 0.1 2.0 1.0 0.1 0.1 2.0 0.0 0.0 0.0 2.0 1.0 0.0 0.0 } { set testid btree2-$testno.8.$num2 set hash [md5file test2.bt] do_test $testid.0 { set ::b [btree_open test2.bt] set ::c2 [btree_cursor $::b 2 1] set ::c3 [btree_cursor $::b 3 1] set ::c4 [btree_cursor $::b 4 1] set ::c5 [btree_cursor $::b 5 1] set ::c6 [btree_cursor $::b 6 1] check_invariants } {} set cnt 6 for {set i 2} {$i<=6} {incr i} { if {[lindex [btree_cursor_dump [set ::c$i]] 0]!=$i} {incr cnt} } do_test $testid.1 { btree_begin_transaction $::b lindex [btree_pager_stats $::b] 1 } $cnt # exec cp test2.bt test2.bt.bu1 do_test $testid.2 [subst { random_changes $n $I $K $D }] {} do_test $testid.3 { check_invariants } {} do_test $testid.4 { btree_close_cursor $::c2 btree_close_cursor $::c3 btree_close_cursor $::c4 btree_close_cursor $::c5 btree_close_cursor $::c6 btree_rollback $::b md5file test2.bt } $hash # exec cp test2.bt test2.bt.bu2 btree_begin_transaction $::b set ::c2 [btree_cursor $::b 2 1] set ::c3 [btree_cursor $::b 3 1] set ::c4 [btree_cursor $::b 4 1] set ::c5 [btree_cursor $::b 5 1] set ::c6 [btree_cursor $::b 6 1] do_test $testid.5 [subst { random_changes $n $I $K $D }] {} do_test $testid.6 { check_invariants } {} do_test $testid.7 { btree_commit $::b check_invariants } {} set hash [md5file test2.bt] do_test $testid.8 { btree_close_cursor $::c2 btree_close_cursor $::c3 btree_close_cursor $::c4 btree_close_cursor $::c5 btree_close_cursor $::c6 lindex [btree_pager_stats $::b] 1 } {0} do_test $testid.9 { btree_close $::b set ::b [btree_open test2.bt] set ::c2 [btree_cursor $::b 2 1] set ::c3 [btree_cursor $::b 3 1] set ::c4 [btree_cursor $::b 4 1] set ::c5 [btree_cursor $::b 5 1] set ::c6 [btree_cursor $::b 6 1] check_invariants } {} do_test $testid.10 { btree_close_cursor $::c2 btree_close_cursor $::c3 btree_close_cursor $::c4 btree_close_cursor $::c5 btree_close_cursor $::c6 lindex [btree_pager_stats $::b] 1 } {0} do_test $testid.11 { btree_close $::b } {} incr num2 } incr testno set ::b [btree_open test2.bt]} # Testing is complete. Shut everything down.#do_test btree-999.1 { lindex [btree_pager_stats $::b] 1} {0}do_test btree-999.2 { btree_close $::b} {}do_test btree-999.3 { file delete -force test2.bt file exists test2.bt-journal} {0}} ;# end if( not mem: and has pager_open command );finish_test
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -