Files
TransPyC/ViperBTests/ViperZipfileProject/App/main.vp
T

320 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# main.vp — ViperZipfileProject:zipfile 模块读写测试
#
# 覆盖:
# 1) ZipFile("w") 写入 4 个成员(deflate 文本 / deflate 大文件 / stored 伪随机 / 空文件)
# 2) ZipFile("r") 读回: 成员数/名称/大小/compress_size 检查 + 逐成员内容比对
# (CRC32 校验由 zipfile.read 内部完成)
# 3) 损坏 stored 成员 payload 1 字节 → 再次 read 应返回 None 且错误码 ZIP_ERR_CRC
import t, c
from stdint import *
import stdio
import stdlib
import string
import zipfile
import w32.win32base as wb
import w32.win32file as wf
ZIP_PATH: str = "zip_roundtrip.zip"
BIG_LEN: t.CSizeT = 100000
RND_LEN: t.CSizeT = 5000
HELLO_TEXT: str = "Hello, Viper Zipfile!\n"
fail_count: t.CInt = 0
def chk(name: str, cond: t.CInt):
global fail_count
if cond:
stdio.printf("[PASS] %s\n", name)
else:
fail_count += 1
stdio.printf("[FAIL] %s\n", name)
stdio.fflush(None) # 崩溃时也保证已打印行可见(stdout 全缓冲)
def StrLen(s: bytes) -> t.CSizeT:
n: t.CSizeT = 0
while s is not None and c.Deref(s) != 0:
n += 1
s += 1
return n
def fill_pattern(buf: bytes, length: t.CSizeT, kind: t.CInt):
i: t.CSizeT = 0
while i < length:
if kind == 1:
# 'A'..'G' 循环 — 高度可压缩
buf[i] = 65 + (i % 7)
else:
# 伪随机 — 供 ZIP_STORED 成员
buf[i] = (i * 37 + 11) & 0xFF
i += 1
def WriteZip() -> t.CInt:
stdio.printf("\n--- 1) 写 zip(4 个成员) ---\n")
wf.DeleteFileA(ZIP_PATH)
w: zipfile.ZipFile | t.CPtr = zipfile.ZipFile(ZIP_PATH, "w", zipfile.ZIP_DEFLATED)
if w.ok == 0:
stdio.printf("open(w) failed: %s\n", zipfile.error())
return 1
hello: bytes = stdlib.malloc(64)
if not hello:
stdio.printf("malloc hello failed\n")
w.close()
return 1
string.strcpy(hello, HELLO_TEXT)
hello_len: t.CSizeT = StrLen(hello)
big: bytes = stdlib.malloc(BIG_LEN)
if not big:
stdio.printf("malloc big failed\n")
stdlib.free(hello)
w.close()
return 1
fill_pattern(big, BIG_LEN, 1)
rnd: bytes = stdlib.malloc(RND_LEN)
if not rnd:
stdio.printf("malloc rnd failed\n")
stdlib.free(hello)
stdlib.free(big)
w.close()
return 1
fill_pattern(rnd, RND_LEN, 0)
rc: t.CInt = w.write("hello.txt", hello, hello_len, zipfile.ZIP_DEFLATED)
chk("write hello.txt (deflate)", rc == zipfile.ZIP_ERR_OK)
if rc != zipfile.ZIP_ERR_OK:
stdio.printf(" hello err: %s\n", zipfile.error())
rc = w.write("big.bin", big, BIG_LEN, zipfile.ZIP_DEFLATED)
chk("write big.bin (deflate, 100000B)", rc == zipfile.ZIP_ERR_OK)
if rc != zipfile.ZIP_ERR_OK:
stdio.printf(" big err: %s\n", zipfile.error())
rc = w.write("random.bin", rnd, RND_LEN, zipfile.ZIP_STORED)
chk("write random.bin (stored)", rc == zipfile.ZIP_ERR_OK)
if rc != zipfile.ZIP_ERR_OK:
stdio.printf(" random err: %s\n", zipfile.error())
rc = w.write("empty.txt", None, 0, zipfile.ZIP_DEFLATED)
chk("write empty.txt (deflate, 0B)", rc == zipfile.ZIP_ERR_OK)
if rc != zipfile.ZIP_ERR_OK:
stdio.printf(" empty err: %s\n", zipfile.error())
rc = w.close()
chk("close(w)", rc == zipfile.ZIP_ERR_OK)
if rc != zipfile.ZIP_ERR_OK:
stdio.printf(" close err: %s\n", zipfile.error())
# close 后用裸文件句柄独立确认落盘大小(不依赖 zipfile 读逻辑)
ph: wb.HANDLE = wf.CreateFileA(
ZIP_PATH, wf.GENERIC_READ,
wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE | wf.FILE_SHARE_DELETE,
None, wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
if ph == wb.INVALID_HANDLE_VALUE:
stdio.printf("probe open failed: err=%u\n", wb.GetLastError())
else:
pli: wb.LARGE_INTEGER = wb.LARGE_INTEGER()
if wf.GetFileSizeEx(ph, t.CPtr(c.Addr(pli))) != 0:
stdio.printf("zip file size on disk = %lld\n", pli.QuadPart)
wb.CloseHandle(ph)
stdlib.free(hello)
stdlib.free(big)
stdlib.free(rnd)
return 0
def CheckReadBack(buf: bytes, expect: bytes, length: t.CSizeT) -> t.CInt:
# buf: zipfile.read 返回的解压缓冲;expect: 期望内容
if buf is None:
return 0
if length == 0:
return 1
return t.CInt(string.memcmp(buf, expect, length) == 0)
def ReadZip() -> t.CInt:
stdio.printf("\n--- 2) 读回校验 ---\n")
r: zipfile.ZipFile | t.CPtr = zipfile.ZipFile(ZIP_PATH, "r")
if r.ok == 0:
stdio.printf("open(r) failed: %s\n", zipfile.error())
return 1
chk("entry count == 4", r.info_count == 4)
i: t.CSizeT = 0
while i < r.info_count:
stdio.printf(" [%d] %s\n", t.CInt(i), r.namelist_at(i))
i += 1
# ---- hello.txt:deflate 文本 ----
info: zipfile.ZipInfo | t.CPtr = r.getinfo("hello.txt")
chk("getinfo(hello.txt)", info is not None)
hello_expect: bytes = stdlib.malloc(64)
string.strcpy(hello_expect, HELLO_TEXT)
exp_len: t.CSizeT = StrLen(hello_expect)
if info is not None:
chk("hello file_size == 22", info.file_size == exp_len)
chk("hello compress_size > 0", info.compress_size > 0)
olen: t.CSizeT = 0
data: bytes = r.read("hello.txt", c.Addr(olen))
chk("read(hello.txt)", data is not None)
chk("hello content match", CheckReadBack(data, hello_expect, olen) == 1)
stdlib.free(data)
stdlib.free(hello_expect)
# ---- big.bin:deflate 大文件 ----
info = r.getinfo("big.bin")
chk("getinfo(big.bin)", info is not None)
big_expect: bytes = stdlib.malloc(BIG_LEN)
fill_pattern(big_expect, BIG_LEN, 1)
if info is not None:
chk("big file_size == 100000", info.file_size == BIG_LEN)
chk("big compress_size < file_size", info.compress_size < info.file_size)
chk("big crc != 0", info.crc32 != 0)
olen = 0
data = r.read("big.bin", c.Addr(olen))
chk("read(big.bin)", data is not None)
chk("big content match", data is not None and olen == BIG_LEN
and CheckReadBack(data, big_expect, BIG_LEN) == 1)
stdlib.free(data)
stdlib.free(big_expect)
# ---- random.bin:stored ----
info = r.getinfo("random.bin")
chk("getinfo(random.bin)", info is not None)
rnd_expect: bytes = stdlib.malloc(RND_LEN)
fill_pattern(rnd_expect, RND_LEN, 0)
if info is not None:
chk("random file_size == 5000", info.file_size == RND_LEN)
chk("random stored: compress_size == file_size", info.compress_size == info.file_size)
olen = 0
data = r.read("random.bin", c.Addr(olen))
chk("read(random.bin)", data is not None)
chk("random content match", data is not None and olen == RND_LEN
and CheckReadBack(data, rnd_expect, RND_LEN) == 1)
stdlib.free(data)
stdlib.free(rnd_expect)
# ---- empty.txt:空成员 ----
info = r.getinfo("empty.txt")
chk("getinfo(empty.txt)", info is not None)
olen = 0
data = r.read("empty.txt", c.Addr(olen))
chk("read(empty.txt) ok", data is not None)
chk("empty length == 0", olen == 0)
stdlib.free(data)
# ---- 不存在成员 ----
olen = 0
data = r.read("no_such.txt", c.Addr(olen))
chk("read(missing) returns None", data is None)
chk("read(missing) code NOT_FOUND", zipfile.error_code() == zipfile.ZIP_ERR_NOT_FOUND)
rc: t.CInt = r.close()
chk("close(r)", rc == zipfile.ZIP_ERR_OK)
return 0
def CorruptStoredCheck() -> t.CInt:
stdio.printf("\n--- 3) 损坏 stored 成员后 CRC 校验 ---\n")
r: zipfile.ZipFile | t.CPtr = zipfile.ZipFile(ZIP_PATH, "r")
if r.ok == 0:
stdio.printf("open(r) failed: %s\n", zipfile.error())
return 1
info: zipfile.ZipInfo | t.CPtr = r.getinfo("random.bin")
chk("getinfo(random.bin)", info is not None)
if info is None or info.compress_size < 2:
stdio.printf("cannot locate stored member payload\n")
r.close()
return 1
# 本地头无 extra 时,数据起点 = header_offset + 30 + 名字长
start: t.CSizeT = t.CSizeT(info.header_offset) + 30 + info.name_len
flip_pos: t.CSizeT = start + info.compress_size - 1
# 方案: 整文件读入内存 → 翻转 stored 数据尾字节 → 整体重写(复用已验证的读写循环)
hr: wb.HANDLE = wf.CreateFileA(
ZIP_PATH, wf.GENERIC_READ,
wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE | wf.FILE_SHARE_DELETE,
None, wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
chk("open R", hr != wb.INVALID_HANDLE_VALUE)
if hr != wb.INVALID_HANDLE_VALUE:
fsz_li: wb.LARGE_INTEGER = wb.LARGE_INTEGER()
if wf.GetFileSizeEx(hr, t.CPtr(c.Addr(fsz_li))) == 0 or fsz_li.QuadPart <= 0:
chk("get file size", 0)
wb.CloseHandle(hr)
r.close()
return 1
fsz: t.CSizeT = t.CSizeT(fsz_li.QuadPart)
whole: BYTEPTR = BYTEPTR(stdlib.malloc(fsz))
if not whole:
wb.CloseHandle(hr)
r.close()
return 1
# 读整个文件
done: t.CSizeT = 0
rd_ok: t.CInt = 1
while done < fsz:
rdgot: ULONG = 0
if wf.ReadFile(hr, whole + done, ULONG(fsz - done), c.Addr(rdgot), None) == 0 or rdgot == 0:
rd_ok = 0
break
done += t.CSizeT(rdgot)
wb.CloseHandle(hr)
chk("read whole file", rd_ok == 1)
if rd_ok == 1 and flip_pos < fsz:
stdio.printf(" flip stored data byte at %lld (size=%lld)\n", flip_pos, fsz_li.QuadPart)
whole[flip_pos] = (whole[flip_pos] ^ 0xFF) & 0xFF
# 重写整个文件
hw: wb.HANDLE = wf.CreateFileA(
ZIP_PATH, wf.GENERIC_WRITE,
wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE | wf.FILE_SHARE_DELETE,
None, wf.CREATE_ALWAYS, wf.FILE_ATTRIBUTE_NORMAL, None)
chk("open W rewrite", hw != wb.INVALID_HANDLE_VALUE)
if hw != wb.INVALID_HANDLE_VALUE:
wr_ok: t.CInt = 1
wdone: t.CSizeT = 0
while wdone < fsz:
wrgot: ULONG = 0
if wf.WriteFile(hw, whole + wdone, ULONG(fsz - wdone), c.Addr(wrgot), None) == 0 or wrgot == 0:
wr_ok = 0
break
wdone += t.CSizeT(wrgot)
wb.CloseHandle(hw)
chk("rewrite file with flipped byte", wr_ok == 1)
else:
chk("flip position in bounds", 0)
stdlib.free(whole)
r.close()
z2: zipfile.ZipFile | t.CPtr = zipfile.ZipFile(ZIP_PATH, "r")
chk("reopen after corruption", z2.ok == 1)
olen: t.CSizeT = 0
data: bytes = z2.read("random.bin", c.Addr(olen))
chk("corrupted read returns None", data is None)
chk("error code == ZIP_ERR_CRC", zipfile.error_code() == zipfile.ZIP_ERR_CRC)
stdio.printf(" last error: %s\n", zipfile.error())
if data is not None:
stdlib.free(data)
z2.close()
return 0
@t.CExport
def main() -> int:
stdio.printf("ViperZipfileProject: zipfile 模块读写测试\n")
if WriteZip() != 0:
return 1
if ReadZip() != 0:
return 1
if CorruptStoredCheck() != 0:
return 1
wf.DeleteFileA(ZIP_PATH)
stdio.printf("\n失败数: %d\n", fail_count)
if fail_count == 0:
stdio.printf("全部通过\n")
return t.CInt(fail_count != 0)