1# 2017 December 9 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 13set testdir [file dirname $argv0] 14source $testdir/tester.tcl 15set testprefix zipfile 16 17ifcapable !vtab { 18 finish_test; return 19} 20if {[catch {load_static_extension db zipfile} error]} { 21 puts "Skipping zipfile tests, hit load error: $error" 22 finish_test; return 23} 24 25proc readfile {f} { 26 set fd [open $f] 27 fconfigure $fd -translation binary -encoding binary 28 set data [read $fd] 29 close $fd 30 set data 31} 32 33if {$::tcl_platform(platform)=="unix" && [catch {exec unzip}]==0} { 34 set ::UNZIP 1 35 load_static_extension db fileio 36 proc do_unzip {file} { 37 forcedelete test_unzip 38 file mkdir test_unzip 39 exec unzip -d test_unzip $file 40 41 set res [db eval { 42 SELECT replace(name,'test_unzip/',''),mode,mtime,data 43 FROM fsdir('test_unzip') 44 WHERE name!='test_unzip' 45 ORDER BY name 46 }] 47 set res 48 } 49} 50 51 52# The argument is a blob (not a hex string) containing a zip archive. 53# This proc removes the extended timestamp fields from the archive 54# and returns the result. 55# 56proc remove_timestamps {blob} { 57 set hex [binary encode hex $blob] 58 set hex [string map {55540500 00000500} $hex] 59 binary decode hex $hex 60} 61 62 63# Argument $file is the name of a zip archive on disk. This function 64# executes test cases to check that the results of each of the following 65# are the same: 66# 67# SELECT * FROM zipfile($file) 68# SELECT * FROM zipfile( readfile($file) ) 69# SELECT * FROM zipfile( 70# (SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file)) 71# ) 72# 73proc do_zipfile_blob_test {tn file} { 74 75 db func r readfile 76 set q1 {SELECT name,mode,mtime,method,quote(data) FROM zipfile($file)} 77 set q2 {SELECT name,mode,mtime,method,quote(data) FROM zipfile( r($file) )} 78 set q3 {SELECT name,mode,mtime,method,quote(data) FROM zipfile( 79 ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) 80 )} 81 82 83 set r1 [db eval $q1] 84 set r2 [db eval $q2] 85 set r3 [db eval $q3] 86 #puts $r1 87 #puts $r2 88 #puts $r3 89 90 uplevel [list do_test $tn.1 [list set {} $r2] $r1] 91 uplevel [list do_test $tn.2 [list set {} $r3] $r1] 92} 93 94# Argument $file is a zip file on disk. This command runs tests to: 95# 96# 1. Unpack the archive with unix command [unzip] and compare the 97# results to reading the same archive using the zipfile() table 98# valued function. 99# 100# 2. Creates a new archive with the same contents using the zipfile() 101# aggregate function as follows: 102# 103# SELECT writefile('test_unzip.zip', 104# ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) 105# ); 106# 107# Then tests that unpacking the new archive using [unzip] produces 108# the same results as in (1). 109# 110proc do_unzip_test {tn file} { 111 if {[info vars ::UNZIP]==""} { return } 112 db func sss strip_slash 113 114 db eval { 115 SELECT writefile('test_unzip.zip', 116 ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) 117 ); 118 } 119 120 set r1 [db eval { 121 SELECT sss(name),mode,mtime,data FROM zipfile($file) ORDER BY name 122 }] 123 set r2 [do_unzip $file] 124 set r3 [do_unzip test_unzip.zip] 125 126 uplevel [list do_test $tn.1 [list set {} $r2] $r1] 127 uplevel [list do_test $tn.2 [list set {} $r3] $r1] 128} 129proc strip_slash {in} { regsub {/$} $in {} } 130 131proc do_zip_tests {tn file} { 132 uplevel do_zipfile_blob_test $tn.1 $file 133 uplevel do_unzip_test $tn.2 $file 134} 135 136forcedelete test.zip 137do_execsql_test 1.0 { 138 CREATE VIRTUAL TABLE temp.zz USING zipfile('test.zip'); 139 PRAGMA table_info(zz); 140} { 141 0 name {} 1 {} 1 142 1 mode {} 0 {} 0 143 2 mtime {} 0 {} 0 144 3 sz {} 0 {} 0 145 4 rawdata {} 0 {} 0 146 5 data {} 0 {} 0 147 6 method {} 0 {} 0 148} 149 150do_catchsql_test 1.1.0.1 { 151 INSERT INTO zz(name, mode, mtime, sz, rawdata, method) 152 VALUES('f.txt', '-rw-r--r--', 1000000000, 5, 'abcde', 0); 153} {1 {constraint failed}} 154do_catchsql_test 1.1.0.1 { 155 INSERT INTO zz(name, mtime, sz, rawdata, method) 156 VALUES('g.txt', 1000000002, 5, '12345', 0); 157} {1 {constraint failed}} 158 159do_execsql_test 1.1.1 { 160 INSERT INTO zz(name, mode, mtime, data, method) 161 VALUES('f.txt', '-rw-r--r--', 1000000000, 'abcde', 0); 162} 163do_execsql_test 1.1.2 { 164 INSERT INTO zz(name, mode, mtime, data, method) 165 VALUES('g.txt', NULL, 1000000002, '12345', 0); 166} 167 168do_execsql_test 1.2 { 169 SELECT name, mtime, data FROM zipfile('test.zip') 170} { 171 f.txt 1000000000 abcde 172 g.txt 1000000002 12345 173} 174do_zip_tests 1.2a test.zip 175 176do_execsql_test 1.3 { 177 INSERT INTO zz(name, mode, mtime, data) VALUES('h.txt', 178 '-rw-r--r--', 1000000004, 'aaaaaaaaaabbbbbbbbbb' 179 ); 180} 181do_zip_tests 1.3a test.zip 182 183do_execsql_test 1.4 { 184 SELECT name, mtime, data, method FROM zipfile('test.zip'); 185} { 186 f.txt 1000000000 abcde 0 187 g.txt 1000000002 12345 0 188 h.txt 1000000004 aaaaaaaaaabbbbbbbbbb 8 189} 190 191ifcapable json1 { 192 do_execsql_test 1.4.1 { 193 SELECT name, json_extract( zipfile_cds(z) , '$.crc32')!=0 194 FROM zipfile('test.zip'); 195 } { 196 f.txt 1 197 g.txt 1 198 h.txt 1 199 } 200} 201 202do_execsql_test 1.5.1 { 203 BEGIN; 204 INSERT INTO zz(name, mode, mtime, data, method) 205 VALUES('i.txt', '-rw-r--r--', 1000000006, 'zxcvb', 0); 206 SELECT name FROM zz; 207 COMMIT; 208} {f.txt g.txt h.txt i.txt} 209do_execsql_test 1.5.2 { 210 SELECT name FROM zz; 211} {f.txt g.txt h.txt i.txt} 212do_execsql_test 1.5.3 { 213 SELECT data FROM zz WHERE name='i.txt'; 214} {zxcvb} 215 216do_execsql_test 1.6.0 { 217 DELETE FROM zz WHERE name='g.txt'; 218 SELECT name FROM zz; 219} {f.txt h.txt i.txt} 220 221do_execsql_test 1.6.1 { 222 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 223} { 224 f.txt 33188 1000000000 abcde 0 225 h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8 226 i.txt 33188 1000000006 zxcvb 0 227} 228do_zip_tests 1.6.1a test.zip 229 230do_execsql_test 1.6.2 { 231 UPDATE zz SET mtime=4 WHERE name='i.txt'; 232 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 233} { 234 f.txt 33188 1000000000 abcde 0 235 h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8 236 i.txt 33188 4 zxcvb 0 237} 238 239do_execsql_test 1.6.3 { 240 UPDATE zz SET mode='-rw-r--r-x' WHERE name='h.txt'; 241 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 242} { 243 f.txt 33188 1000000000 abcde 0 244 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 245 i.txt 33188 4 zxcvb 0 246} 247do_zip_tests 1.6.3a test.zip 248 249do_execsql_test 1.6.4 { 250 UPDATE zz SET name = 'blue.txt' WHERE name='f.txt'; 251 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 252} { 253 blue.txt 33188 1000000000 abcde 0 254 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 255 i.txt 33188 4 zxcvb 0 256} 257do_zip_tests 1.6.4a test.zip 258 259do_execsql_test 1.6.5 { 260 UPDATE zz SET data = 'edcba' WHERE name='blue.txt'; 261 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 262} { 263 blue.txt 33188 1000000000 edcba 0 264 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 265 i.txt 33188 4 zxcvb 0 266} 267 268do_execsql_test 1.6.6 { 269 UPDATE zz SET mode=NULL, data = NULL WHERE name='blue.txt'; 270 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 271} { 272 blue.txt/ 16877 1000000000 {} 0 273 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 274 i.txt 33188 4 zxcvb 0 275} 276 277do_catchsql_test 1.6.7 { 278 UPDATE zz SET data=NULL WHERE name='i.txt' 279} {1 {constraint failed}} 280do_execsql_test 1.6.8 { 281 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 282} { 283 blue.txt/ 16877 1000000000 {} 0 284 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 285 i.txt 33188 4 zxcvb 0 286} 287 288#------------------------------------------------------------------------- 289db close 290forcedelete test.zip 291reset_db 292load_static_extension db fileio 293load_static_extension db zipfile 294do_execsql_test 2.1 { 295 CREATE VIRTUAL TABLE zzz USING zipfile('test.zip'); 296 INSERT INTO zzz(name, mode) VALUES('dirname', 'drwxr-xr-x'); 297 SELECT name, mode, data FROM zzz; 298} {dirname/ 16877 {}} 299do_execsql_test 2.2 { 300 INSERT INTO zzz(name, data) VALUES('dirname2', NULL); 301 INSERT INTO zzz(name, data) VALUES('dirname2/file1.txt', 'abcdefghijklmnop'); 302 SELECT name, mode, data FROM zzz; 303} { 304 dirname/ 16877 {} 305 dirname2/ 16877 {} 306 dirname2/file1.txt 33188 abcdefghijklmnop 307} 308 309do_catchsql_test 2.3 { 310 UPDATE zzz SET name = 'dirname3' WHERE name = 'dirname/'; 311} {0 {}} 312do_execsql_test 2.4 { 313 SELECT name, mode, data FROM zzz; 314} { 315 dirname3/ 16877 {} 316 dirname2/ 16877 {} 317 dirname2/file1.txt 33188 abcdefghijklmnop 318} 319do_zip_tests 2.4a test.zip 320 321# If on unix, check that the [unzip] utility can unpack our archive. 322# 323if {$::tcl_platform(platform)=="unix"} { 324 do_test 2.5.1 { 325 forcedelete dirname 326 forcedelete dirname2 327 set rc [catch { exec unzip test.zip > /dev/null } msg] 328 list $rc $msg 329 } {0 {}} 330 do_test 2.5.2 { file isdir dirname3 } 1 331 do_test 2.5.3 { file isdir dirname2 } 1 332 do_test 2.5.4 { file isdir dirname2/file1.txt } 0 333 do_test 2.5.5 { 334 set fd [open dirname2/file1.txt] 335 set data [read $fd] 336 close $fd 337 set data 338 } {abcdefghijklmnop} 339} 340 341#------------------------------------------------------------------------- 342reset_db 343forcedelete test.zip 344load_static_extension db zipfile 345 346do_execsql_test 3.0 { 347 CREATE VIRTUAL TABLE temp.x1 USING zipfile('test.zip'); 348 INSERT INTO x1(name, data) VALUES('dir1/', NULL); 349 INSERT INTO x1(name, data) VALUES('file1', '1234'); 350 INSERT INTO x1(name, data) VALUES('dir1/file2', '5678'); 351} 352foreach {tn fname} { 353 1 dir1 354 2 file1 355 3 dir1/file2 356} { 357 do_catchsql_test 3.1.$tn.0 { 358 INSERT INTO x1(name, data) VALUES($fname, NULL); 359 } {1 {constraint failed}} 360 do_catchsql_test 3.1.$tn.1 { 361 INSERT INTO x1(name, data) VALUES($fname || '/', NULL); 362 } {1 {constraint failed}} 363 do_catchsql_test 3.1.$tn.2 { 364 INSERT INTO x1(name, data) VALUES($fname, 'abcd'); 365 } {1 {constraint failed}} 366} 367 368do_catchsql_test 3.2 { 369 SELECT rowid FROM x1 370} {1 {no such column: rowid}} 371 372#------------------------------------------------------------------------- 373# Test some error conditions. 374# 375do_catchsql_test 4.1 { 376 CREATE VIRTUAL TABLE yyy USING zipfile(); 377} {1 {zipfile constructor requires one argument}} 378do_catchsql_test 4.2 { 379 CREATE VIRTUAL TABLE yyy USING zipfile('test.zip', 'test.zip'); 380} {1 {zipfile constructor requires one argument}} 381 382#-------------------------------------------------------------------------- 383 384db func rt remove_timestamps 385do_execsql_test 5.0 { 386 WITH c(name,mtime,data) AS ( 387 SELECT 'a.txt', 946684800, 'abc' 388 ) 389 SELECT name,mtime,data FROM zipfile( 390 ( SELECT rt( zipfile(name,NULL,mtime,data) ) FROM c ) 391 ) 392} { 393 a.txt 946684800 abc 394} 395 396if {[info vars ::UNZIP]!=""} { 397ifcapable datetime { 398 load_static_extension db fileio 399 forcedelete test1.zip test2.zip 400 do_test 6.0 { 401 execsql { 402 WITH c(name,mtime,data) AS ( 403 SELECT 'a.txt', 946684800, 'abc' UNION ALL 404 SELECT 'b.txt', 1000000000, 'abc' UNION ALL 405 SELECT 'c.txt', 1111111000, 'abc' 406 ) 407 SELECT writefile('test1.zip', rt( zipfile(name, NULL, mtime, data) ) ), 408 writefile('test2.zip', ( zipfile(name, NULL, mtime, data) ) ) 409 FROM c; 410 } 411 forcedelete test_unzip 412 file mkdir test_unzip 413 exec unzip -d test_unzip test1.zip 414 415 db eval { 416 SELECT name, strftime('%s', mtime, 'unixepoch', 'localtime') 417 FROM fsdir('test_unzip') WHERE name!='test_unzip' 418 ORDER BY name 419 } 420 } [list {*}{ 421 test_unzip/a.txt 946684800 422 test_unzip/b.txt 1000000000 423 test_unzip/c.txt 1111111000 424 }] 425 426 do_test 6.1 { 427 forcedelete test_unzip 428 file mkdir test_unzip 429 exec unzip -d test_unzip test2.zip 430 431 db eval { 432 SELECT name, mtime 433 FROM fsdir('test_unzip') WHERE name!='test_unzip' 434 ORDER BY name 435 } 436 } [list {*}{ 437 test_unzip/a.txt 946684800 438 test_unzip/b.txt 1000000000 439 test_unzip/c.txt 1111111000 440 }] 441} 442} 443 444 445finish_test 446 447