# 2017 December 9
#
# 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.
#
#***********************************************************************
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix zipfile

ifcapable !vtab {
  finish_test; return
}
if {[catch {load_static_extension db zipfile} error]} {
  puts "Skipping zipfile tests, hit load error: $error"
  finish_test; return
}

proc readfile {f} {
  set fd [open $f]
  fconfigure $fd -translation binary -encoding binary
  set data [read $fd]
  close $fd
  set data
}

if {$::tcl_platform(platform)=="unix" && [catch {exec unzip}]==0} {
  set ::UNZIP 1
  load_static_extension db fileio
  proc do_unzip {file} {
    forcedelete test_unzip
    file mkdir test_unzip
    exec unzip -d test_unzip $file
  
    set res [db eval { 
      SELECT replace(name,'test_unzip/',''),mode,mtime,data 
      FROM fsdir('test_unzip') 
      WHERE name!='test_unzip'
      ORDER BY name
    }]
    set res
  }
}

# Argument $file is the name of a zip archive on disk. This function
# executes test cases to check that the results of each of the following 
# are the same:
#
#         SELECT * FROM zipfile($file)
#         SELECT * FROM zipfile( readfile($file) )
#         SELECT * FROM zipfile( 
#           (SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file))
#         )
#
proc do_zipfile_blob_test {tn file} {

  db func r readfile
  set q1 {SELECT name,mode,mtime,method,quote(data) FROM zipfile($file)}
  set q2 {SELECT name,mode,mtime,method,quote(data) FROM zipfile( r($file) )}
  set q3 {SELECT name,mode,mtime,method,quote(data) FROM zipfile(
    ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) )
  )}


  set r1 [db eval $q1]
  set r2 [db eval $q2]
  set r3 [db eval $q3]
  #puts $r1
  #puts $r2
  #puts $r3

  uplevel [list do_test $tn.1 [list set {} $r2] $r1]
  uplevel [list do_test $tn.2 [list set {} $r3] $r1]
}

# Argument $file is a zip file on disk. This command runs tests to:
#
#   1. Unpack the archive with unix command [unzip] and compare the 
#      results to reading the same archive using the zipfile() table
#      valued function.
#
#   2. Creates a new archive with the same contents using the zipfile()
#      aggregate function as follows:
#
#      SELECT writefile('test_unzip.zip',
#          ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) )
#      );
#
#      Then tests that unpacking the new archive using [unzip] produces
#      the same results as in (1).
#
proc do_unzip_test {tn file} {
  if {[info vars ::UNZIP]==""} { return }
  db func sss strip_slash

  db eval {
    SELECT writefile('test_unzip.zip',
        ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) )
    );
  }

  set r1 [db eval { 
    SELECT sss(name),mode,mtime,data FROM zipfile($file) ORDER BY name
  }]
  set r2 [do_unzip $file]
  set r3 [do_unzip test_unzip.zip]

  uplevel [list do_test $tn.1 [list set {} $r2] $r1]
  uplevel [list do_test $tn.2 [list set {} $r3] $r1]
}
proc strip_slash {in} { regsub {/$} $in {} }

proc do_zip_tests {tn file} {
  uplevel do_zipfile_blob_test $tn.1 $file
  uplevel do_unzip_test $tn.2 $file
}

forcedelete test.zip
do_execsql_test 1.0 {
  CREATE VIRTUAL TABLE temp.zz USING zipfile('test.zip');
  PRAGMA table_info(zz);
} {
  0 name {} 1 {} 1 
  1 mode {} 0 {} 0 
  2 mtime {} 0 {} 0 
  3 sz {} 0 {} 0 
  4 rawdata {} 0 {} 0
  5 data {} 0 {} 0
  6 method {} 0 {} 0
}

do_catchsql_test 1.1.0.1 {
  INSERT INTO zz(name, mode, mtime, sz, rawdata, method) 
  VALUES('f.txt', '-rw-r--r--', 1000000000, 5, 'abcde', 0);
} {1 {constraint failed}}
do_catchsql_test 1.1.0.1 {
  INSERT INTO zz(name, mtime, sz, rawdata, method) 
  VALUES('g.txt', 1000000002, 5, '12345', 0);
} {1 {constraint failed}}

do_execsql_test 1.1.1 {
  INSERT INTO zz(name, mode, mtime, data, method) 
  VALUES('f.txt', '-rw-r--r--', 1000000000, 'abcde', 0);
}
do_execsql_test 1.1.2 {
  INSERT INTO zz(name, mode, mtime, data, method) 
  VALUES('g.txt', NULL, 1000000002, '12345', 0);
}

do_execsql_test 1.2 {
  SELECT name, mtime, data FROM zipfile('test.zip')
} {
  f.txt 1000000000 abcde 
  g.txt 1000000002 12345
}
do_zip_tests 1.2a test.zip

do_execsql_test 1.3 {
  INSERT INTO zz(name, mode, mtime, data) VALUES('h.txt', 
    '-rw-r--r--', 1000000004, 'aaaaaaaaaabbbbbbbbbb'
  );
}
do_zip_tests 1.3a test.zip

do_execsql_test 1.4 {
  SELECT name, mtime, data, method FROM zipfile('test.zip');
} {
  f.txt 1000000000 abcde 0
  g.txt 1000000002 12345 0
  h.txt 1000000004 aaaaaaaaaabbbbbbbbbb 8
}

ifcapable json1 {
  do_execsql_test 1.4.1 {
    SELECT name, json_extract( zipfile_cds(z) , '$.crc32')!=0
    FROM zipfile('test.zip');
  } {
    f.txt 1
    g.txt 1
    h.txt 1
  }
}

do_execsql_test 1.5.1 {
  BEGIN;
    INSERT INTO zz(name, mode, mtime, data, method)
    VALUES('i.txt', '-rw-r--r--', 1000000006, 'zxcvb', 0);
    SELECT name FROM zz;
  COMMIT;
} {f.txt g.txt h.txt i.txt}
do_execsql_test 1.5.2 {
  SELECT name FROM zz;
} {f.txt g.txt h.txt i.txt}
do_execsql_test 1.5.3 {
  SELECT data FROM zz WHERE name='i.txt';
} {zxcvb}

do_execsql_test 1.6.0 {
  DELETE FROM zz WHERE name='g.txt';
  SELECT name FROM zz;
} {f.txt h.txt i.txt}

do_execsql_test 1.6.1 {
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  f.txt 33188 1000000000 abcde 0
  h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 1000000006 zxcvb 0
}
do_zip_tests 1.6.1a test.zip

do_execsql_test 1.6.2 {
  UPDATE zz SET mtime=4 WHERE name='i.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  f.txt 33188 1000000000 abcde 0
  h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_execsql_test 1.6.3 {
  UPDATE zz SET mode='-rw-r--r-x' WHERE name='h.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  f.txt 33188 1000000000 abcde 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}
do_zip_tests 1.6.3a test.zip

do_execsql_test 1.6.4 {
  UPDATE zz SET name = 'blue.txt' WHERE name='f.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt 33188 1000000000 abcde 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}
do_zip_tests 1.6.4a test.zip

do_execsql_test 1.6.5 {
  UPDATE zz SET data = 'edcba' WHERE name='blue.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt 33188 1000000000 edcba 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_execsql_test 1.6.6 {
  UPDATE zz SET mode=NULL, data = NULL WHERE name='blue.txt';
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt/ 16877 1000000000 {} 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

do_catchsql_test 1.6.7 {
  UPDATE zz SET data=NULL WHERE name='i.txt'
} {1 {constraint failed}}
do_execsql_test 1.6.8 {
  SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
  blue.txt/ 16877 1000000000 {} 0
  h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
  i.txt 33188 4 zxcvb 0
}

#-------------------------------------------------------------------------
db close
forcedelete test.zip
reset_db
load_static_extension db fileio
load_static_extension db zipfile
do_execsql_test 2.1 {
  CREATE VIRTUAL TABLE zzz USING zipfile('test.zip');
  INSERT INTO zzz(name, mode) VALUES('dirname', 'drwxr-xr-x');
  SELECT name, mode, data FROM zzz;
} {dirname/ 16877 {}}
do_execsql_test 2.2 {
  INSERT INTO zzz(name, data) VALUES('dirname2', NULL);
  INSERT INTO zzz(name, data) VALUES('dirname2/file1.txt', 'abcdefghijklmnop');
  SELECT name, mode, data FROM zzz;
} {
  dirname/ 16877 {}
  dirname2/ 16877 {}
  dirname2/file1.txt 33188 abcdefghijklmnop
}

do_catchsql_test 2.3 {
  UPDATE zzz SET name = 'dirname3' WHERE name = 'dirname/';
} {0 {}}
do_execsql_test 2.4 {
  SELECT name, mode, data FROM zzz;
} {
  dirname3/ 16877 {}
  dirname2/ 16877 {}
  dirname2/file1.txt 33188 abcdefghijklmnop
}
do_zip_tests 2.4a test.zip

# If on unix, check that the [unzip] utility can unpack our archive.
#
if {$::tcl_platform(platform)=="unix"} {
  do_test 2.5.1 {
    forcedelete dirname
    forcedelete dirname2
    set rc [catch { exec unzip test.zip > /dev/null } msg]
    list $rc $msg
  } {0 {}}
  do_test 2.5.2 { file isdir dirname3 } 1
  do_test 2.5.3 { file isdir dirname2 } 1
  do_test 2.5.4 { file isdir dirname2/file1.txt } 0
  do_test 2.5.5 { 
    set fd [open dirname2/file1.txt]
    set data [read $fd]
    close $fd
    set data
  } {abcdefghijklmnop}
}

#-------------------------------------------------------------------------
reset_db
forcedelete test.zip
load_static_extension db zipfile

do_execsql_test 3.0 {
  CREATE VIRTUAL TABLE temp.x1 USING zipfile('test.zip');
  INSERT INTO x1(name, data) VALUES('dir1/', NULL);
  INSERT INTO x1(name, data) VALUES('file1', '1234');
  INSERT INTO x1(name, data) VALUES('dir1/file2', '5678');
}
foreach {tn fname} {
  1 dir1
  2 file1
  3 dir1/file2
} {
  do_catchsql_test 3.1.$tn.0 {
    INSERT INTO x1(name, data) VALUES($fname, NULL);
  } {1 {constraint failed}}
  do_catchsql_test 3.1.$tn.1 {
    INSERT INTO x1(name, data) VALUES($fname || '/', NULL);
  } {1 {constraint failed}}
  do_catchsql_test 3.1.$tn.2 {
    INSERT INTO x1(name, data) VALUES($fname, 'abcd');
  } {1 {constraint failed}}
}

do_catchsql_test 3.2 {
  SELECT rowid FROM x1
} {1 {no such column: rowid}}

#-------------------------------------------------------------------------
# Test some error conditions.
#
do_catchsql_test 4.1 {
  CREATE VIRTUAL TABLE yyy USING zipfile();
} {1 {zipfile constructor requires one argument}}
do_catchsql_test 4.2 {
  CREATE VIRTUAL TABLE yyy USING zipfile('test.zip', 'test.zip');
} {1 {zipfile constructor requires one argument}}



finish_test

