1# 2021-03-06 2# 3# The author disclaims copyright to this source code. In place of 4# a legal notice, here is a blessing: 5# 6# May you do good and not evil. 7# May you find forgiveness for yourself and forgive others. 8# May you share freely, never taking more than you give. 9# 10#*********************************************************************** 11# 12# This file implements tests for the appendvfs extension. 13# 14# Tests performed: 15# avfs-1.0. Test that an appendvfs DB can be added to an empty (ZLF) file. 16# avfs-1.1. Test that the DB can be read with correct content upon reopen. 17# avfs-1.2. Test that an appendvfs DB can be added to a simple text file. 18# avfs-1.3. Test that the DB can be read with correct content upon reopen. 19# avfs-1.4. Test that appended DB is aligned to default page boundary. 20# avfs-2.1. Test that the simple text file retains its initial text. 21# avfs-3.1. Test that the appendvfs can grow and shrink, remaining intact. 22# avfs-3.2. Test that appendvfs is intact after grow/shrink/close/reopen. 23# avfs-4.1. Test shell's ability to append to a non-appendvfs file. 24# avfs-4.2. Test shell's ability to append to empty or nonexistent file. 25# avfs-4.3. Test shell's ability to reopen and alter an appendvfs file. 26# avfs-5.1. Test appendvfs refusal to open too-tiny DB appended onto ZLF. 27# avfs-5.2. Test appendvfs refusal to open too-tiny DB appended on other. 28# ... 29# (more to come) 30 31set testdir [file dirname $argv0] 32source $testdir/tester.tcl 33set ::testprefix avfs 34set CLI [test_find_cli] 35db close 36# forcedelete test.db 37 38load_static_extension db appendvfs 39 40set ::fa avfs.adb 41set ::fza avfs.sdb 42forcedelete $::fa $::fza 43set ::result {} 44 45proc shellDoesAr {} { 46 set shdo "sh_app1.sql" 47 forcedelete $shdo 48 set fd [open $shdo w] 49 puts $fd ".help\n.q" 50 close $fd 51 set res [catchcmd "-batch -cmd \".read $shdo\""] 52 return [regexp {^.archive} [lindex $res 1]] 53} 54 55set ::vf "&vfs=apndvfs" 56 57# Return file offset of appendvfs portion of a file, or {} if none such. 58proc fosAvfs {fname} { 59 if {[file size $fname] < 25} { 60 return {} 61 } 62 if {[catch {set fd [open $fname rb]}]} { 63 return {} 64 } 65 seek $fd -25 end 66 set am [read $fd 17] 67 set ao [read $fd 8] 68 close $fd 69 if {$am ne "Start-Of-SQLite3-"} { 70 return {} 71 } 72 binary scan $ao "W" rvo 73 return $rvo 74} 75 76do_test 1.0 { 77 set results {} 78 set out [open $::fza wb] 79 close $out 80 sqlite3 adb "file:$::fza?mode=rwc$::vf" -uri 1 81 adb eval { 82 PRAGMA page_size=1024; 83 PRAGMA cache_size=10; 84 CREATE TABLE t1(a TEXT); 85 INSERT INTO t1 VALUES ('dog'),('cat'); 86 SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a); 87 } { lappend results $pets } 88 adb close 89 lappend results [fosAvfs $fza] 90 set ::result [join $results " | "] 91} {cat,dog | 0} 92 93do_test 1.1 { 94 set results {} 95 sqlite3 adb "file:$::fza?mode=rw$::vf" -uri 1 96 adb eval { 97 SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a DESC); 98 } { lappend results $pets } 99 adb close 100 set ::result [join $results " | "] 101} {dog,cat} 102 103do_test 1.2 { 104 set results {} 105 set out [open $::fa wb] 106 set ::tlo { "Just some text," "and more text," "ending at 3 lines." } 107 puts $out [join $::tlo "\n"] 108 close $out 109 set adbSz [file size $::fa] 110 sqlite3 adb "file:$::fa?mode=rwc$::vf" -uri 1 111 adb eval { 112 PRAGMA auto_vacuum = 0; 113 PRAGMA page_size=512; 114 PRAGMA cache_size=0; 115 CREATE TABLE t1(a TEXT); 116 INSERT INTO t1 VALUES ('dog'),('cat'),('pig'); 117 SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a); 118 } { lappend results $pets } 119 adb close 120 set adaSz [file size $::fa] 121 lappend results "Bytes before/after $adbSz/$adaSz" 122 set ::result [join $results " | "] 123} {cat,dog,pig | Bytes before/after 50/5145} 124 125do_test 1.3 { 126 set results {} 127 sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 128 adb eval { 129 SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a DESC); 130 } { lappend results $pets } 131 adb close 132 set ::result [join $results " | "] 133} {pig,dog,cat} 134 135do_test 1.4 { 136 set ::result [fosAvfs $fa] 137} {4096} 138 139do_test 2.1 { 140 set in [open $::fa r] 141 set tli {} 142 for {set i [llength $::tlo]} {$i > 0} {incr i -1} { 143 lappend tli [gets $in] 144 } 145 close $in 146 if { [join $tli ":"] ne [join $::tlo ":"] } { 147 set ::result "Appendee changed." 148 } else { 149 set ::result "Appendee intact." 150 } 151} {Appendee intact.} 152 153# Set of repeatable random integers for a couple tests. 154proc rint {v} { 155 return [::tcl::mathfunc::int [expr $v * 100000]] 156} 157array set ::randints [list 0 [rint [::tcl::mathfunc::srand 0]]] 158for {set i 1} {$i < 10000} {incr i} { 159 set ::randints($i) [rint [::tcl::mathfunc::rand]] 160} 161 162do_test 3.1 { 163 set results {} 164 sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 165 adb eval { 166 DROP TABLE t1; 167 PRAGMA cache_size=10; 168 CREATE TABLE ri (i INTEGER); 169 BEGIN; 170 } 171 for {set i 0} {$i < 10000} {incr i} { 172 set r $::randints($i) 173 set s $::randints([incr i]) 174 set t $::randints([incr i]) 175 set u $::randints([incr i]) 176 set v $::randints([incr i]) 177 adb eval { 178 INSERT INTO ri VALUES ($r),($s),($t),($u),($v) 179 } 180 } 181 adb eval { 182 COMMIT; 183 SELECT integrity_check as ic FROM pragma_integrity_check(); 184 } { lappend results $ic } 185 set adbSz [file size $::fa] 186 set qr {} 187 adb eval { 188 SELECT count(*) as ic FROM ri; 189 DELETE FROM ri WHERE (i % 50) <> 25; 190 SELECT integrity_check as ic FROM pragma_integrity_check(); 191 VACUUM; 192 SELECT integrity_check as ic FROM pragma_integrity_check(); 193 SELECT count(*) as ic FROM ri; 194 } { lappend qr $ic } 195 adb close 196 set adaSz [file size $::fa] 197 set adba [expr ($adbSz + 0.1)/$adaSz] 198 # lappend results $adbSz $adaSz 199 set results [concat $results [lrange $qr 0 2]] 200 lappend results [expr {$adba > 10.0 && $adba < 20.0}] 201 set ::result [join $results " | "] 202} {ok | 10000 | ok | ok | 1} 203 204do_test 3.2 { 205 set results {} 206 sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1 207 adb eval { 208 SELECT integrity_check as ic FROM pragma_integrity_check(); 209 } { lappend results $ic } 210 adb close 211 set ::result [join $results " | "] 212} {ok} 213 214set ::cliDoesAr [shellDoesAr] 215 216do_test 4.1 { 217 set shdo "sh_app1.sql" 218 set shod "sh_app1.adb" 219 forcedelete $shdo $shod 220 set ofd [open $shdo w] 221 if {$::cliDoesAr} { 222 puts $ofd ".ar -c" 223 } else { 224 puts $ofd "pragma page_size=512;" 225 puts $ofd "create table sqlar (a);" 226 } 227 puts $ofd ".tables" 228 puts $ofd ".q" 229 close $ofd 230 set ofd [open $shod wb] 231 puts $ofd "Some text." 232 close $ofd 233 set res [catchcmd "-append -batch -init $shdo $shod" ""] 234 lappend res [fosAvfs $shod] 235 forcedelete $shdo $shod 236 set ::result [join $res " | "] 237} {0 | sqlar | 4096} 238 239do_test 4.2 { 240 set shdo "sh_app1.sql" 241 set shod "sh_app1.adb" 242 forcedelete $shdo $shod 243 set ofd [open $shdo w] 244 if {$::cliDoesAr} { 245 puts $ofd ".ar -c" 246 } else { 247 puts $ofd "pragma page_size=512;" 248 puts $ofd "create table sqlar (a);" 249 } 250 puts $ofd ".tables" 251 puts $ofd ".q" 252 close $ofd 253 set ofd [open $shod wb] 254 close $ofd 255 set res [catchcmd "-append -batch -init $shdo $shod" ""] 256 lappend res [fosAvfs $shod] 257 forcedelete $shdo ; # Leave $shod for next test. 258 set ::result [join $res " | "] 259} {0 | sqlar | 0} 260 261do_test 4.3 { 262 set shdo "sh_app1.sql" 263 set shod "sh_app1.adb" ; # Same as test 4.2, reusing ADB. 264 forcedelete $shdo 265 set ofd [open $shdo w] 266 if {$::cliDoesAr} { 267 puts $ofd ".ar -u $shdo" 268 puts $ofd "select count(*) from sqlar where name = '$shdo';" 269 } else { 270 puts $ofd "insert into sqlar values (1);" 271 puts $ofd "select count(*) from sqlar;" 272 } 273 puts $ofd ".q" 274 close $ofd 275 set res [catchcmd "-append -batch -init $shdo $shod" ""] 276 sqlite3 adb "file:$shod?mode=rw$::vf" -uri 1 277 adb eval { 278 SELECT count(*) as n FROM sqlar 279 } { lappend res $n } 280 adb close 281 forcedelete $shdo $shod; 282 set ::result [join $res " | "] 283} {0 | 1 | 1} 284 285do_test 5.1 { 286 set fake "faketiny.sdb" 287 forcedelete $fake 288 set ofd [open $fake wb] 289 puts -nonewline $ofd "SQLite format 3" 290 puts -nonewline $ofd [binary format "c" 0] 291 puts -nonewline $ofd "Start-Of-SQLite3-" 292 puts -nonewline $ofd [binary format "W" 0] 293 close $ofd 294 if {[catch {sqlite3 adb "file:$fake?mode=rw$::vf" -uri 1}]} { 295 set res "Open failed." 296 } else { 297 adb close 298 set res "Opened when should not." 299 } 300 forcedelete $fake 301 set ::result $res 302} {Open failed.} 303 304do_test 5.2 { 305 set fake "faketiny.sdb" 306 forcedelete $fake 307 set ofd [open $fake wb] 308 set fakeAppendee "Dog ate my homework.\n" 309 puts -nonewline $ofd $fakeAppendee 310 puts -nonewline $ofd "SQLite format 3" 311 puts -nonewline $ofd [binary format "c" 0] 312 puts -nonewline $ofd "Start-Of-SQLite3-" 313 puts -nonewline $ofd [binary format "W" [string length $fakeAppendee]] 314 close $ofd 315 if {[catch {sqlite3 adb "file:$fake?mode=rw$::vf" -uri 1}]} { 316 set res "Open failed." 317 } else { 318 adb close 319 set res "Opened when should not." 320 } 321 forcedelete $fake 322 set ::result $res 323} {Open failed.} 324 325forcedelete $::fa $::fza 326 327unset -nocomplain ::fa ::fza ::tlo ::result ::randints ::cliDoesAr 328 329finish_test 330