RollFor/libs/vanilla/LibDeflate/tests/Test.lua
2025-03-30 23:09:58 +02:00

3251 lines
106 KiB
Lua
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--[[
zlib License
(C) 2018-2020 Haoqian He
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--]]
-- Run this tests at the folder where LibDeflate.lua located, like this.
-- lua tests/Test.lua
-- Don't run two "tests/Test.lua" at the same time,
-- because they will conflict!!!
package.path = ("?.lua;tests/LibCompress/?.lua;")..(package.path or "")
do
local test_lua = io.open("tests/Test.lua")
assert(test_lua
, "Must run this script in the root folder of LibDeflate repository")
test_lua:close()
end
local old_globals = {}
for k, v in pairs(_G) do
old_globals[k] = v
end
local LibDeflate = require("LibDeflate")
for k, v in pairs(_G) do
assert(v == old_globals[k], "LibDeflate global leak at key: "..tostring(k))
end
for k, v in pairs(old_globals) do
assert(v == _G[k], "LibDeflate global leak at key: "..tostring(k))
end
-- UnitTests
local lu = require("luaunit")
assert(lu)
local assert = assert
local loadstring = loadstring or load
local math = math
local string = string
local table = table
local collectgarbage = collectgarbage
local os = os
local type = type
local io = io
local print = print
local tostring = tostring
local string_char = string.char
local string_byte = string.byte
local string_len = string.len
local string_sub = string.sub
local unpack = unpack or table.unpack
local table_insert = table.insert
local table_concat = table.concat
math.randomseed(0) -- I don't like true random tests that I cant 100% reproduce.
local _pow2 = {}
do
local pow = 1
for i = 0, 32 do
_pow2[i] = pow
pow = pow * 2
end
end
local function DeepCopy(obj)
local SearchTable = {} -- luacheck: ignore
local function Func(object)
if type(object) ~= "table" then
return object
end
local NewTable = {}
SearchTable[object] = NewTable
for k, v in pairs(object) do
NewTable[Func(k)] = Func(v)
end
return setmetatable(NewTable, getmetatable(object))
end
return Func(obj)
end
local function GetTableSize(t)
local size = 0
for _, _ in pairs(t) do
size = size + 1
end
return size
end
local HexToString
local HalfByteToHex
do
local _byte0 = string_byte("0", 1)
local _byte9 = string_byte("9", 1)
local _byteA = string_byte("A", 1)
local _byteF = string_byte("F", 1)
local _bytea = string_byte("a", 1)
local _bytef = string_byte("f", 1)
function HexToString(str)
local t = {}
local val = 1
for i=1, str:len()+1 do
local b = string_byte(str, i, i) or -1
if b >= _byte0 and b <= _byte9 then
val = val*16 + b - _byte0
elseif b >= _byteA and b <= _byteF then
val = val*16 + b - _byteA + 10
elseif b >= _bytea and b <= _bytef then
val = val*16 + b - _bytea + 10
elseif val ~= 1 and val < 32 then -- one digit followed by delimiter
val = val + 240 -- make it look like two digits
end
if val > 255 then
t[#t+1] = string_char(val % 256)
val = 1
end
end
return table.concat(t)
end
assert(HexToString("f") == string_char(15))
assert(HexToString("1f") == string_char(31))
assert(HexToString("1f 2") == string_char(31)..string_char(2))
assert(HexToString("1f 22") == string_char(31)..string_char(34))
assert(HexToString("F") == string_char(15))
assert(HexToString("1F") == string_char(31))
assert(HexToString("1F 2") == string_char(31)..string_char(2))
assert(HexToString("1F 22") == string_char(31)..string_char(34))
assert(HexToString("a") == string_char(10))
assert(HexToString("1a") == string_char(26))
assert(HexToString("1a 90") == string_char(26)..string_char(144))
assert(HexToString("1a 9") == string_char(26)..string_char(9))
assert(HexToString("A") == string_char(10))
assert(HexToString("1A") == string_char(26))
assert(HexToString("1A 09") == string_char(26)..string_char(9))
assert(HexToString("1A 00") == string_char(26)..string_char(0))
function HalfByteToHex(half_byte)
assert (half_byte >= 0 and half_byte < 16)
if half_byte < 10 then
return string_char(_byte0 + half_byte)
else
return string_char(_bytea + half_byte-10)
end
end
end
local function StringToHex(str)
if not str then
return "nil"
end
local tmp = {}
for i = 1, str:len() do
local b = string_byte(str, i, i)
if b < 16 then
tmp[#tmp+1] = "0"..HalfByteToHex(b)
else
tmp[#tmp+1] = HalfByteToHex((b-b%16)/16)..HalfByteToHex(b%16)
end
end
return table.concat(tmp, " ")
end
assert (StringToHex("\000"), "00")
assert (StringToHex("\000\255"), "00 ff")
assert (StringToHex(HexToString("05 e0 81 91 24 cb b2 2c 49 e2 0f 2e 8b 9a"
.." 47 56 9f fb fe ec d2 ff 1f"))
== "05 e0 81 91 24 cb b2 2c 49 e2 0f 2e 8b 9a 47 56 9f fb fe ec d2 ff 1f")
-- Return a string with limited size
local function StringForPrint(str)
if str:len() < 101 then
return str
else
return str:sub(1, 101)..(" (%d more characters not shown)")
:format(str:len()-101)
end
end
local function OpenFile(filename, mode)
local f = io.open(filename, mode)
lu.assertNotNil(f, ("Cannot open the file: %s, with mode: %s")
:format(filename, mode))
return f
end
local function GetFileData(filename)
local f = OpenFile(filename, "rb")
local str = f:read("*all")
f:close()
return str
end
local function WriteToFile(filename, data)
local f = io.open(filename, "wb")
lu.assertNotNil(f, ("Cannot open the file: %s, with mode: %s")
:format(filename, "wb"))
f:write(data)
f:flush()
f:close()
end
local function GetLimitedRandomString(strlen)
local randoms = {}
for _=1, 7 do
randoms[#randoms+1] = string.char(math.random(1, 255))
end
local tmp = {}
for _=1, strlen do
tmp[#tmp+1] = randoms[math.random(1, 7)]
end
return table.concat(tmp)
end
local function GetRandomString(strlen)
local tmp = {}
for _=1, strlen do
tmp[#tmp+1] = string_char(math.random(0, 255))
end
return table.concat(tmp)
end
-- Get a random string with at least 256 len which includes all characters
local function GetRandomStringUniqueChars(strlen)
local taken = {}
local tmp = {}
for i=0, (strlen < 256) and strlen-1 or 255 do
local rand = math.random(1, 256-i)
local count = 0
for j=0, 255 do
if (not taken[j]) then
count = count + 1
end
if count == rand then
taken[j] = true
tmp[#tmp+1] = string_char(j)
break
end
end
end
if strlen > 256 then
for _=1, strlen-256 do
table_insert(tmp, math.random(1, #tmp+1)
, string_char(math.random(0, 255)))
end
end
return table_concat(tmp)
end
assert(GetRandomStringUniqueChars(3):len() == 3)
assert(GetRandomStringUniqueChars(255):len() == 255)
assert(GetRandomStringUniqueChars(256):len() == 256)
assert(GetRandomStringUniqueChars(500):len() == 500)
do
local taken = {}
local str = GetRandomStringUniqueChars(256)
for i=1, 256 do
local byte = string_byte(str, i, i)
assert(not taken[byte])
taken[byte] = true
end
end
-- Repeatedly collect memory garbarge until memory usage no longer changes
local function FullMemoryCollect()
local memory_used = collectgarbage("count")
local last_memory_used
local stable_count = 0
repeat
last_memory_used = memory_used
collectgarbage("collect")
memory_used = collectgarbage("count")
if memory_used >= last_memory_used then
stable_count = stable_count + 1
else
stable_count = 0
end
until stable_count == 10
-- Stop full memory collect until memory does not decrease for 10 times.
end
local function RunProgram(program, input_filename, stdout_filename)
local stderr_filename = stdout_filename..".stderr"
local status, _, ret = os.execute(program.." "..input_filename
.. "> "..stdout_filename.." 2> "..stderr_filename)
local returned_status
if type(status) == "number" then -- lua 5.1
returned_status = status
else -- Lua 5.2/5.3
returned_status = ret
if not status and ret == 0 then
returned_status = -1
-- Lua bug on Windows when the returned value is -1, ret is 0
end
end
local stdout = GetFileData(stdout_filename)
local stderr = GetFileData(stderr_filename)
return returned_status, stdout, stderr
end
local function AssertLongStringEqual(actual, expected, msg)
if actual ~= expected then
lu.assertNotNil(actual, ("%s actual is nil"):format(msg or ""))
lu.assertNotNil(expected, ("%s expected is nil"):format(msg or ""))
local diff_index = 1
for i=1, math.max(expected:len(), actual:len()) do
if string_byte(actual, i, i) ~= string_byte(expected, i, i) then
diff_index = i
break
end
end
local actual_msg = string.format(
"%s actualLen: %d, expectedLen:%d, first difference at: %d,"
.." actualByte: %s, expectByte: %s", msg or "", actual:len()
, expected:len(), diff_index,
string.byte(actual, diff_index) or "nil",
string.byte(expected, diff_index) or "nil")
lu.assertTrue(false, actual_msg)
end
end
local function MemCheckAndBenchmarkFunc(lib, func_name, ...)
local memory_before
local memory_running
local memory_after
local start_time
local elapsed_time
local ret
FullMemoryCollect()
memory_before = math.floor(collectgarbage("count")*1024)
FullMemoryCollect()
start_time = os.clock()
elapsed_time = -1
local repeat_count = 0
while elapsed_time < 0.015 do
ret = {lib[func_name](lib, ...)}
elapsed_time = os.clock() - start_time
repeat_count = repeat_count + 1
end
memory_running = math.floor(collectgarbage("count")*1024)
FullMemoryCollect()
memory_after = math.floor(collectgarbage("count")*1024)
local memory_used = memory_running - memory_before
local memory_leaked = memory_after - memory_before
return memory_leaked, memory_used
, elapsed_time*1000/repeat_count, unpack(ret)
end
local function GetFirstBlockType(compressed_data, isZlib)
local first_block_byte_index = 1
if isZlib then
local byte2 = string.byte(compressed_data, 2, 2)
local has_dict = ((byte2-byte2%32)/32)%2
if has_dict == 1 then
first_block_byte_index = 7
else
first_block_byte_index = 3
end
end
local first_byte = string.byte(compressed_data
, first_block_byte_index, first_block_byte_index)
local bit3 = first_byte % 8
return (bit3 - bit3 % 2) / 2
end
local function PutRandomBitsInPaddingBits(compressed_data, padding_bitlen)
if padding_bitlen > 0 then
local len = #compressed_data
local last_byte = string.byte(compressed_data, len)
local random_last_byte = math.random(0, 255)
random_last_byte = random_last_byte
- random_last_byte % _pow2[8-padding_bitlen]
random_last_byte = random_last_byte
+ last_byte % _pow2[8-padding_bitlen]
compressed_data = compressed_data:sub(1, len-1)
..string.char(random_last_byte)
end
return compressed_data
end
local dictionary32768_str = GetFileData("tests/dictionary32768.txt")
local dictionary32768 = LibDeflate:CreateDictionary(dictionary32768_str
, 32768, 4072834167)
local _CheckCompressAndDecompressCounter = 0
local function CheckCompressAndDecompress(string_or_filename, is_file, levels
, strategy, output_prefix)
-- For 100% code coverage
if _CheckCompressAndDecompressCounter % 3 == 0 then
LibDeflate.internals.InternalClearCache()
end
if _CheckCompressAndDecompressCounter % 2 == 0 then
-- Init cache table in these functions
-- , to help memory leak check in the following codes.
LibDeflate:EncodeForWoWAddonChannel("")
LibDeflate:EncodeForWoWChatChannel("")
else
LibDeflate:DecodeForWoWAddonChannel("")
LibDeflate:DecodeForWoWChatChannel("")
end
_CheckCompressAndDecompressCounter = _CheckCompressAndDecompressCounter + 1
local origin
if is_file then
origin = GetFileData(string_or_filename)
else
origin = string_or_filename
end
FullMemoryCollect()
local total_memory_before = math.floor(collectgarbage("count")*1024)
do
if levels == "all" then
levels = {0,1,2,3,4,5,6,7,8,9}
else
levels = levels or {1}
end
local compress_filename
local decompress_filename
if output_prefix then
compress_filename = output_prefix..".compress"
decompress_filename = output_prefix..".decompress"
else
if is_file then
compress_filename = string_or_filename..".compress"
else
compress_filename = "tests/string.compress"
end
decompress_filename = compress_filename..".decompress"
end
for i=1, #levels+1 do -- also test level == nil
local level = levels[i]
local configs = {level = level, strategy = strategy}
print(
(">>>>> %s: %s size: %d B Level: %s Strategy: %s")
:format(is_file and "File" or "String",
string_or_filename:sub(1, 40), origin:len()
,tostring(level), tostring(strategy)
))
local compress_to_run = {
{"CompressDeflate", origin, configs},
{"CompressDeflateWithDict", origin, dictionary32768
, configs},
{"CompressZlib", origin, configs},
{"CompressZlibWithDict", origin, dictionary32768, configs},
}
for j, compress_running in ipairs(compress_to_run) do
-- Compress by raw deflate
local compress_func_name = compress_running[1]
local compress_memory_leaked, compress_memory_used
, compress_time, compress_data, compress_pad_bitlen =
MemCheckAndBenchmarkFunc(LibDeflate
, unpack(compress_running))
if compress_running[1]:find("Deflate") then
lu.assertTrue(0 <= compress_pad_bitlen
and compress_pad_bitlen < 8
, compress_func_name)
-- put random value in the padding bits,
-- to see if it is still okay to decompress
else
lu.assertEquals(compress_pad_bitlen, 0
, compress_func_name)
end
-- Test encoding
local compress_data_WoW_addon_encoded =
LibDeflate:EncodeForWoWAddonChannel(compress_data)
AssertLongStringEqual(
LibDeflate:DecodeForWoWAddonChannel(
compress_data_WoW_addon_encoded), compress_data
, compress_func_name)
local compress_data_data_WoW_chat_encoded =
LibDeflate:EncodeForWoWChatChannel(compress_data)
AssertLongStringEqual(
LibDeflate:DecodeForWoWChatChannel(
compress_data_data_WoW_chat_encoded), compress_data
, compress_func_name)
-- Put random bits in the padding bits of compressed data.
-- to see if decompression still works.
compress_data = PutRandomBitsInPaddingBits(compress_data
, compress_pad_bitlen)
local isZlib = compress_func_name:find("Zlib")
if strategy == "fixed" then
lu.assertEquals(GetFirstBlockType(compress_data, isZlib)
, (level == 0) and 0 or 1,
compress_func_name.." "..tostring(level))
elseif strategy == "dynamic" then
lu.assertEquals(GetFirstBlockType(compress_data, isZlib)
, (level == 0) and 0 or 2,
compress_func_name.." "..tostring(level))
elseif strategy == "huffman_only" then -- luacheck: ignore
-- Emtpy
elseif strategy == nil then -- luacheck: ignore
-- Empty
else
lu.assertTrue(false, "Unexpected strategy: "
..tostring(strategy))
end
WriteToFile(compress_filename, compress_data)
if not compress_running[1] == "CompressDeflate" then
local returnedStatus_puff, stdout_puff =
RunProgram("puff -w ", compress_filename
, decompress_filename)
lu.assertEquals(returnedStatus_puff, 0
, compress_func_name
.." puff decompression failed with code "
..returnedStatus_puff)
AssertLongStringEqual(stdout_puff, origin
, "puff fails with "..compress_func_name)
end
local decompress_to_run = {
{"DecompressDeflate", compress_data},
{"DecompressDeflateWithDict", compress_data
, dictionary32768, configs},
{"DecompressZlib", compress_data, configs},
{"DecompressZlibWithDict", compress_data
, dictionary32768, configs},
}
lu.assertEquals(#decompress_to_run, #compress_to_run)
local zdeflate_decompress_to_run = {
"zdeflate -d <",
"zdeflate -d --dict tests/dictionary32768.txt <",
"zdeflate --zlib -d <",
"zdeflate --zlib -d --dict tests/dictionary32768.txt <",
}
lu.assertEquals(#zdeflate_decompress_to_run, #compress_to_run)
-- Try decompress by zdeflate
-- zdeflate is a C program calling zlib library
-- which is modifed from zlib example.
-- zdeflate can do all compression and decompression doable
-- by LibDeflate (except encode and decode)
local returnedStatus_zdeflate, stdout_zdeflate
, stderr_zdeflate =
RunProgram(zdeflate_decompress_to_run[j], compress_filename
, decompress_filename)
lu.assertEquals(returnedStatus_zdeflate, 0
, compress_func_name
..":zdeflate decompression failed with msg "
..stderr_zdeflate)
AssertLongStringEqual(stdout_zdeflate, origin
, compress_func_name
.."zdeflate decompress result not match origin string.")
-- Try decompress by LibDeflate
local decompress_memory_leaked, decompress_memory_used,
decompress_time, decompress_data,
decompress_unprocess_byte =
MemCheckAndBenchmarkFunc(LibDeflate
, unpack(decompress_to_run[j]))
AssertLongStringEqual(decompress_data, origin
, compress_func_name
.." LibDeflate decompress result not match origin string.")
lu.assertEquals(decompress_unprocess_byte, 0
, compress_func_name
.." Unprocessed bytes after LibDeflate decompression "
..tostring(decompress_unprocess_byte))
print(
("%s: Size : %d B,Time: %.3f ms, "
.."Speed: %.0f KB/s, Memory: %d B,"
.." Mem/input: %.2f, (memleak?: %d B) padbit: %d\n")
:format(compress_func_name
, compress_data:len(), compress_time
, compress_data:len()/compress_time
, compress_memory_used
, compress_memory_used/origin:len()
, compress_memory_leaked
, compress_pad_bitlen
),
("%s: cRatio: %.2f,Time: %.3f ms"
..", Speed: %.0f KB/s, Memory: %d B,"
.." Mem/input: %.2f, (memleak?: %d B)"):format(
decompress_to_run[j][1]
, origin:len()/compress_data:len(), decompress_time
, decompress_data:len()/decompress_time
, decompress_memory_used
, decompress_memory_used/origin:len()
, decompress_memory_leaked
)
)
end
print("")
end
-- Use all avaiable strategies of zdeflate to compress the data
-- , and see if LibDeflate can decompress it.
local tmp_filename = "tests/tmp.tmp"
WriteToFile(tmp_filename, origin)
local zdeflate_level, zdeflate_strategy
local strategies = {"--filter", "--huffman", "--rle"
, "--fix", "--default"}
local unique_compress = {}
local uniques_compress_count = 0
for level=0, 8 do
zdeflate_level = "-"..level
for j=1, #strategies do
zdeflate_strategy = strategies[j]
local status, stdout, stderr =
RunProgram("zdeflate "..zdeflate_level
.." "..zdeflate_strategy
.." < ", tmp_filename, tmp_filename..".out")
lu.assertEquals(status, 0
, ("zdeflate cant compress the file? "
.."stderr: %s level: %s, strategy: %s")
:format(stderr, zdeflate_level, zdeflate_strategy))
if not unique_compress[stdout] then
unique_compress[stdout] = true
uniques_compress_count = uniques_compress_count + 1
local decompressData =
LibDeflate:DecompressDeflate(stdout)
AssertLongStringEqual(decompressData, origin,
("My decompress fail to decompress "
.."at zdeflate level: %s, strategy: %s")
:format(level, zdeflate_strategy))
end
end
end
print(
(">>>>> %s: %s size: %d B\n")
:format(is_file and "File" or "String"
, string_or_filename:sub(1, 40), origin:len()),
("Full decompress coverage test ok. unique compresses: %d\n")
:format(uniques_compress_count),
"\n")
end
FullMemoryCollect()
local total_memory_after = math.floor(collectgarbage("count")*1024)
local total_memory_difference = total_memory_before - total_memory_after
if total_memory_difference > 0 then
local ignore_leak_jit = ""
if _G.jit then
ignore_leak_jit = " (Ignore when the test is run by LuaJIT)"
end
print(
(">>>>> %s: %s size: %d B\n")
:format(is_file and "File" or "String"
, string_or_filename:sub(1, 40), origin:len()),
("Actual Memory Leak in the test: %d"..ignore_leak_jit.."\n")
:format(total_memory_difference),
"\n")
-- ^If above "leak" is very small
-- , it is very likely that it is false positive.
if not _G.jit and total_memory_difference > 64 then
-- Lua JIT has some problems to garbage collect stuffs
-- , so don't consider as failure.
lu.assertTrue(false
, ("Fail the test because too many actual "
.."Memory Leak in the test: %d")
:format(total_memory_difference))
end
end
return 0
end
local function CheckCompressAndDecompressString(str, levels, strategy)
return CheckCompressAndDecompress(str, false, levels, strategy)
end
local function CheckCompressAndDecompressFile(inputFileName, levels, strategy
, output_prefix)
return CheckCompressAndDecompress(inputFileName, true, levels, strategy
, output_prefix)
end
local function CheckDecompressIncludingError(compress, decompress, is_zlib)
assert (is_zlib == true or is_zlib == nil)
local d, decompress_status
if is_zlib then
d, decompress_status = LibDeflate:DecompressZlib(compress)
else
d, decompress_status = LibDeflate:DecompressDeflate(compress)
end
lu.assertTrue(type(d) == "string" or type(d) == "nil")
lu.assertEquals(type(decompress_status), "number")
lu.assertEquals(decompress_status % 1, 0)
if d ~= decompress then
lu.assertTrue(false, ("My decompress does not match expected result."
.."expected: %s, actual: %s, Returned status of decompress: %d")
:format(StringForPrint(StringToHex(d))
, StringForPrint(StringToHex(decompress)), decompress_status))
else
-- Check my decompress result with "puff"
local input_filename = "tests/tmpFile"
local inputFile = io.open(input_filename, "wb")
inputFile:setvbuf("full")
inputFile:write(compress)
inputFile:flush()
inputFile:close()
local returned_status_puff, stdout_puff =
RunProgram("puff -w", input_filename
, input_filename..".decompress")
local returnedStatus_zdeflate, stdout_zdeflate =
RunProgram(is_zlib and "zdeflate --zlib -d <"
or "zdeflate -d <", input_filename, input_filename..".decompress")
if not d then
if not is_zlib then
if returned_status_puff ~= 0
and returnedStatus_zdeflate ~= 0 then
print((">>>> %q cannot be decompress as expected")
:format((StringForPrint(StringToHex(compress)))))
elseif returned_status_puff ~= 0
and returnedStatus_zdeflate == 0 then
lu.assertTrue(false,
(">>>> %q puff error but not zdeflate?")
:format((StringForPrint(StringToHex(compress)))))
elseif returned_status_puff == 0
and returnedStatus_zdeflate ~= 0 then
lu.assertTrue(false,
(">>>> %q zdeflate error but not puff?")
:format((StringForPrint(StringToHex(compress)))))
else
lu.assertTrue(false,
(">>>> %q my decompress error, but not puff or zdeflate")
:format((StringForPrint(StringToHex(compress)))))
end
else
if returnedStatus_zdeflate ~= 0 then
print((">>>> %q cannot be zlib decompress as expected")
:format(StringForPrint(StringToHex(compress))))
else
lu.assertTrue(false,
(">>>> %q my decompress error, but not zdeflate")
:format((StringForPrint(StringToHex(compress)))))
end
end
else
AssertLongStringEqual(d, stdout_zdeflate)
if not is_zlib then
AssertLongStringEqual(d, stdout_puff)
end
print((">>>> %q is decompressed to %q as expected")
:format(StringForPrint(StringToHex(compress))
, StringForPrint(StringToHex(d))))
end
end
end
local function CheckZlibDecompressIncludingError(compress, decompress)
return CheckDecompressIncludingError(compress, decompress, true)
end
local function CreateDictionaryWithoutVerify(str)
-- Dont do this in the real program.
-- Dont calculate adler32 in runtime. Do hardcode it as constant.
-- This is just for test purpose
local dict = LibDeflate:CreateDictionary(str, #str, LibDeflate:Adler32(str))
return dict
end
local function CreateAndCheckDictionary(str)
local strlen = #str
local dictionary = CreateDictionaryWithoutVerify(str)
lu.assertTrue(LibDeflate.internals.IsValidDictionary(dictionary))
for i=1, strlen do
lu.assertEquals(dictionary.string_table[i], string_byte(str, i, i))
end
lu.assertEquals(dictionary.strlen, str:len())
for i=1, strlen-2 do
local hash = string_byte(str, i, i)*65536
+ string_byte(str, i+1, i+1)*256
+ string_byte(str, i+2, i+2)
local hash_chain = dictionary.hash_tables[hash]
lu.assertNotNil(hash_chain, "nil hash_chain?")
local found = false
for j = 1, #hash_chain do
if hash_chain[j] == i-strlen then
found = true
break
end
end
lu.assertTrue(found
, ("hash index %d not found in dictionary hash_table."):format(i))
end
return dictionary
end
-- the input dictionary must can make compressed data smaller.
-- otherwise, set dontCheckEffectivenss
local function CheckDictEffectiveness(str, dictionary, dict_str
, dontCheckEffectiveness)
local configs = {level = 7}
local compress_dict = LibDeflate:CompressDeflateWithDict(str
, dictionary, configs)
local decompressed_dict =
LibDeflate:DecompressDeflateWithDict(compress_dict, dictionary)
AssertLongStringEqual(decompressed_dict, str)
local compress_no_dict = LibDeflate:CompressDeflate(str, configs)
local decompressed_no_dict =
LibDeflate:DecompressDeflate(compress_no_dict)
AssertLongStringEqual(decompressed_no_dict, str)
local byte_smaller_with_dict = compress_no_dict:len()
- compress_dict:len()
if not dontCheckEffectiveness then
lu.assertTrue(byte_smaller_with_dict > 0)
print((">>> %d bytes smaller with (deflate dict) "..
"DICT: %s, DATA: %s")
:format(byte_smaller_with_dict
, StringForPrint(dict_str), StringForPrint(str)))
end
local zlib_compress_dict = LibDeflate:
CompressZlibWithDict(str, dictionary, configs)
local zlib_decompressed_dict =
LibDeflate:DecompressZlibWithDict(zlib_compress_dict, dictionary)
AssertLongStringEqual(zlib_decompressed_dict, str)
local zlib_compress_no_dict = LibDeflate:CompressZlib(str, configs)
local zlib_decompressed_no_dict =
LibDeflate:DecompressZlib(zlib_compress_no_dict)
AssertLongStringEqual(zlib_decompressed_no_dict, str)
local zlib_byte_smaller_with_dict = zlib_compress_no_dict:len()
- zlib_compress_dict:len()
-- for zlib with dict, 4 extra bytes needed to store
-- the adler32 of dictionary
if not dontCheckEffectiveness then
lu.assertTrue(zlib_byte_smaller_with_dict > -4)
print((">>> %d bytes smaller with (zlib dict) DICT: %s DATA: %s")
:format(zlib_byte_smaller_with_dict
, StringForPrint(dict_str), StringForPrint(str)))
end
return compress_dict, compress_no_dict
, zlib_compress_dict, zlib_compress_no_dict
end
-- Commandline
local arg = _G.arg
if arg and #arg >= 1 and type(arg[0]) == "string" then
if #arg >= 2 and arg[1] == "-o" then
-- For testing purpose (test_from_random_files_in_disk.py),
-- check if the file can be opened by lua
local input = arg[2]
local inputFile = io.open(input, "rb")
if not inputFile then
os.exit(1)
end
inputFile.close()
os.exit(0)
elseif #arg >= 3 and arg[1] == "-c" then
-- For testing purpose (test_from_random_files_in_disk.py)
-- , check the if a file can be correctly compress and decompress to origin
os.exit(CheckCompressAndDecompressFile(arg[2], "all", nil
, "tests/tmp"))
end
end
-------------------------------------------------------------------------
-- LibCompress encode code to help verity encode code in LibDeflate -----
-------------------------------------------------------------------------
local LibCompressEncoder = {}
do
local gsub_escape_table = {
['\000'] = "%z",
[('(')] = "%(",
[(')')] = "%)",
[('.')] = "%.",
[('%')] = "%%",
[('+')] = "%+",
[('-')] = "%-",
[('*')] = "%*",
[('?')] = "%?",
[('[')] = "%[",
[(']')] = "%]",
[('^')] = "%^",
[('$')] = "%$"
}
local function escape_for_gsub(str)
return str:gsub("([%z%(%)%.%%%+%-%*%?%[%]%^%$])", gsub_escape_table)
end
function LibCompressEncoder:GetEncodeTable(reservedChars, escapeChars
, mapChars)
reservedChars = reservedChars or ""
escapeChars = escapeChars or ""
mapChars = mapChars or ""
-- select a default escape character
if escapeChars == "" then
return nil, "No escape characters supplied"
end
if #reservedChars < #mapChars then
return nil, "Number of reserved characters must be at least "
.."as many as the number of mapped chars"
end
if reservedChars == "" then
return nil, "No characters to encode"
end
-- list of characters that must be encoded
local encodeBytes = reservedChars..escapeChars..mapChars
-- build list of bytes not available as a suffix to a prefix byte
local taken = {}
for i = 1, string_len(encodeBytes) do
taken[string_sub(encodeBytes, i, i)] = true
end
-- allocate a table to hold encode/decode strings/functions
local codecTable = {}
-- the encoding can be a single gsub,
-- but the decoding can require multiple gsubs
local decode_func_string = {}
local encode_search = {}
local encode_translate = {}
local encode_func
local decode_search = {}
local decode_translate = {}
local decode_func
local c, r, to, from
local escapeCharIndex, escapeChar = 0
-- map single byte to single byte
if #mapChars > 0 then
for i = 1, #mapChars do
from = string_sub(reservedChars, i, i)
to = string_sub(mapChars, i, i)
encode_translate[from] = to
table_insert(encode_search, from)
decode_translate[to] = from
table_insert(decode_search, to)
end
codecTable["decode_search"..tostring(escapeCharIndex)]
= "([".. escape_for_gsub(table_concat(decode_search)).."])"
codecTable["decode_translate"..tostring(escapeCharIndex)] =
decode_translate
table_insert(decode_func_string, "str = str:gsub(self.decode_search"
..tostring(escapeCharIndex)..", self.decode_translate"
..tostring(escapeCharIndex)..");")
end
-- map single byte to double-byte
escapeCharIndex = escapeCharIndex + 1
escapeChar = string_sub(escapeChars, escapeCharIndex, escapeCharIndex)
r = 0 -- suffix char value to the escapeChar
decode_search = {}
decode_translate = {}
for i = 1, string_len(encodeBytes) do
c = string_sub(encodeBytes, i, i)
if not encode_translate[c] then
-- this loop will update escapeChar and r
while r >= 256 or taken[string_char(r)] do -- Defliate patch
-- bug in LibCompress r81
-- while r < 256 and taken[string_char(r)] do
r = r + 1
if r > 255 then -- switch to next escapeChar
if escapeChar == "" then
-- we are out of escape chars and we need more!
return nil, "Out of escape characters"
end
codecTable["decode_search"..tostring(escapeCharIndex)] =
escape_for_gsub(escapeChar)
.."(["..
escape_for_gsub(table_concat(decode_search)).."])"
codecTable["decode_translate"
..tostring(escapeCharIndex)] = decode_translate
table_insert(decode_func_string,
"str = str:gsub(self.decode_search"
..tostring(escapeCharIndex)
..", self.decode_translate"
..tostring(escapeCharIndex)..");")
escapeCharIndex = escapeCharIndex + 1
escapeChar = string_sub(escapeChars
, escapeCharIndex, escapeCharIndex)
r = 0
decode_search = {}
decode_translate = {}
end
end
encode_translate[c] = escapeChar..string_char(r)
table_insert(encode_search, c)
decode_translate[string_char(r)] = c
table_insert(decode_search, string_char(r))
r = r + 1
end
end
if r > 0 then
codecTable["decode_search"..tostring(escapeCharIndex)] =
escape_for_gsub(escapeChar)
.."([".. escape_for_gsub(table_concat(decode_search)).."])"
codecTable["decode_translate"..tostring(escapeCharIndex)] =
decode_translate
table_insert(decode_func_string,
"str = str:gsub(self.decode_search"..tostring(escapeCharIndex)
..", self.decode_translate"..tostring(escapeCharIndex)..");")
end
-- change last line from "str = ...;" to "return ...;";
decode_func_string[#decode_func_string] =
decode_func_string[#decode_func_string]
:gsub("str = (.*);", "return %1;")
decode_func_string = "return function(self, str) "
..table_concat(decode_func_string).." end"
encode_search = "(["
.. escape_for_gsub(table_concat(encode_search)).."])"
decode_search = escape_for_gsub(escapeChars)
.."([".. escape_for_gsub(table_concat(decode_search)).."])"
encode_func = assert(loadstring(
"return function(self, str) "
.."return str:gsub(self.encode_search, "
.."self.encode_translate); end"))()
decode_func = assert(loadstring(decode_func_string))()
codecTable.encode_search = encode_search
codecTable.encode_translate = encode_translate
codecTable.Encode = encode_func
codecTable.decode_search = decode_search
codecTable.decode_translate = decode_translate
codecTable.Decode = decode_func
codecTable.decode_func_string = decode_func_string -- to be deleted
return codecTable
end
-- Addons: Call this only once and reuse the returned
-- table for all encodings/decodings.
function LibCompressEncoder:GetAddonEncodeTable(reservedChars
, escapeChars, mapChars )
reservedChars = reservedChars or ""
escapeChars = escapeChars or ""
mapChars = mapChars or ""
-- Following byte values are not allowed:
-- \000
if escapeChars == "" then
escapeChars = "\001"
end
return self:GetEncodeTable( (reservedChars or "").."\000"
, escapeChars, mapChars)
end
-- Addons: Call this only once and reuse the returned
-- table for all encodings/decodings.
function LibCompressEncoder:GetChatEncodeTable(reservedChars
, escapeChars, mapChars)
reservedChars = reservedChars or ""
escapeChars = escapeChars or ""
mapChars = mapChars or ""
local r = {}
for i = 128, 255 do
table_insert(r, string_char(i))
end
reservedChars = "sS\000\010\013\124%"
..table_concat(r)..(reservedChars or "")
if escapeChars == "" then
escapeChars = "\029\031"
end
if mapChars == "" then
mapChars = "\015\020";
end
return self:GetEncodeTable(reservedChars, escapeChars, mapChars)
end
end
local _libcompress_addon_codec = LibCompressEncoder:GetAddonEncodeTable()
local _libcompress_chat_codec = LibCompressEncoder:GetChatEncodeTable()
-- Check if LibDeflate's encoding works properly
local function CheckEncodeAndDecode(str, reserved_chars, escape_chars
, map_chars)
if reserved_chars then
local encode_decode_table_libcompress =
LibCompressEncoder:GetEncodeTable(reserved_chars
, escape_chars, map_chars)
local encode_decode_table, message =
LibDeflate:CreateCodec(reserved_chars
, escape_chars, map_chars)
if not encode_decode_table then
print(message)
end
local encoded_libcompress = encode_decode_table_libcompress:Encode(str)
local encoded = encode_decode_table:Encode(str)
AssertLongStringEqual(encoded, encoded_libcompress
, "Encoded result does not match libcompress")
AssertLongStringEqual(encode_decode_table:Decode(encoded), str
, "Encoded str cant be decoded to origin")
end
local encoded_addon = LibDeflate:EncodeForWoWAddonChannel(str)
local encoded_addon_libcompress =
_libcompress_addon_codec:Encode(str)
AssertLongStringEqual(encoded_addon, encoded_addon_libcompress
, "Encoded addon channel result does not match libcompress")
AssertLongStringEqual(LibDeflate:DecodeForWoWAddonChannel(encoded_addon)
, str, "Encoded for addon channel str cant be decoded to origin")
local encoded_chat = LibDeflate:EncodeForWoWChatChannel(str)
local encoded_chat_libcompress = _libcompress_chat_codec:Encode(str)
AssertLongStringEqual(encoded_chat, encoded_chat_libcompress
, "Encoded chat channel result does not match libcompress")
AssertLongStringEqual(LibDeflate:DecodeForWoWChatChannel(encoded_chat), str
, "Encoded for chat channel str cant be decoded to origin")
end
--------------------------------------------------------------
-- Actual Tests Start ----------------------------------------
--------------------------------------------------------------
TestBasicStrings = {}
function TestBasicStrings:TestEmpty()
CheckCompressAndDecompressString("", "all")
end
function TestBasicStrings:TestAllLiterals1()
CheckCompressAndDecompressString("ab", "all")
end
function TestBasicStrings:TestAllLiterals2()
CheckCompressAndDecompressString("abcdefgh", "all")
end
function TestBasicStrings:TestAllLiterals3()
local t = {}
for i=0, 255 do
t[#t+1] = string.char(i)
end
local str = table.concat(t)
CheckCompressAndDecompressString(str, "all")
end
function TestBasicStrings:TestRepeat()
CheckCompressAndDecompressString("aaaaaaaaaaaaaaaaaa", "all")
end
function TestBasicStrings:TestLongRepeat()
local repeated = {}
for i=1, 100000 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
TestMyData = {}
function TestMyData:TestItemStrings()
CheckCompressAndDecompressFile("tests/data/itemStrings.txt", "all")
end
function TestMyData:TestSmallTest()
CheckCompressAndDecompressFile("tests/data/smalltest.txt", "all")
end
function TestMyData:TestReconnectData()
CheckCompressAndDecompressFile("tests/data/reconnectData.txt", "all")
end
TestThirdPartySmall = {}
function TestThirdPartySmall:TestEmpty()
CheckCompressAndDecompressFile("tests/data/3rdparty/empty", "all")
end
function TestThirdPartySmall:TestX()
CheckCompressAndDecompressFile("tests/data/3rdparty/x", "all")
end
function TestThirdPartySmall:TestXYZZY()
CheckCompressAndDecompressFile("tests/data/3rdparty/xyzzy", "all")
end
TestThirdPartyMedium = {}
function TestThirdPartyMedium:Test10x10y()
CheckCompressAndDecompressFile("tests/data/3rdparty/10x10y", "all")
end
function TestThirdPartyMedium:TestQuickFox()
CheckCompressAndDecompressFile("tests/data/3rdparty/quickfox", "all")
end
function TestThirdPartyMedium:Test64x()
CheckCompressAndDecompressFile("tests/data/3rdparty/64x", "all")
end
function TestThirdPartyMedium:TestUkkonoona()
CheckCompressAndDecompressFile("tests/data/3rdparty/ukkonooa", "all")
end
function TestThirdPartyMedium:TestMonkey()
CheckCompressAndDecompressFile("tests/data/3rdparty/monkey", "all")
end
function TestThirdPartyMedium:TestRandomChunks()
CheckCompressAndDecompressFile("tests/data/3rdparty/random_chunks"
, "all")
end
function TestThirdPartyMedium:TestGrammerLsp()
CheckCompressAndDecompressFile("tests/data/3rdparty/grammar.lsp"
, "all")
end
function TestThirdPartyMedium:TestXargs1()
CheckCompressAndDecompressFile("tests/data/3rdparty/xargs.1", "all")
end
function TestThirdPartyMedium:TestRandomOrg10KBin()
CheckCompressAndDecompressFile("tests/data/3rdparty/random_org_10k.bin"
, "all")
end
function TestThirdPartyMedium:TestCpHtml()
CheckCompressAndDecompressFile("tests/data/3rdparty/cp.html", "all")
end
function TestThirdPartyMedium:TestBadData1Snappy()
CheckCompressAndDecompressFile("tests/data/3rdparty/baddata1.snappy"
, "all")
end
function TestThirdPartyMedium:TestBadData2Snappy()
CheckCompressAndDecompressFile("tests/data/3rdparty/baddata2.snappy"
, "all")
end
function TestThirdPartyMedium:TestBadData3Snappy()
CheckCompressAndDecompressFile("tests/data/3rdparty/baddata3.snappy"
, "all")
end
function TestThirdPartyMedium:TestSum()
CheckCompressAndDecompressFile("tests/data/3rdparty/sum", "all")
end
Test_64K = {}
function Test_64K:Test64KFile()
CheckCompressAndDecompressFile("tests/data/64k.txt", "all")
end
function Test_64K:Test64KFilePlus1()
CheckCompressAndDecompressFile("tests/data/64kplus1.txt", "all")
end
function Test_64K:Test64KFilePlus2()
CheckCompressAndDecompressFile("tests/data/64kplus2.txt", "all")
end
function Test_64K:Test64KFilePlus3()
CheckCompressAndDecompressFile("tests/data/64kplus3.txt", "all")
end
function Test_64K:Test64KFilePlus4()
CheckCompressAndDecompressFile("tests/data/64kplus4.txt", "all")
end
function Test_64K:Test64KFileMinus1()
CheckCompressAndDecompressFile("tests/data/64kminus1.txt", "all")
end
function Test_64K:Test64KRepeated()
local repeated = {}
for i=1, 65536 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
function Test_64K:Test64KRepeatedPlus1()
local repeated = {}
for i=1, 65536+1 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
function Test_64K:Test64KRepeatedPlus2()
local repeated = {}
for i=1, 65536+2 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
function Test_64K:Test64KRepeatedPlus3()
local repeated = {}
for i=1, 65536+3 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
function Test_64K:Test64KRepeatedPlus4()
local repeated = {}
for i=1, 65536+4 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
function Test_64K:Test64KRepeatedMinus1()
local repeated = {}
for i=1, 65536-1 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
function Test_64K:Test64KRepeatedMinus2()
local repeated = {}
for i=1, 65536-2 do
repeated[i] = "c"
end
CheckCompressAndDecompressString(table.concat(repeated), "all")
end
-- > 64K
TestThirdPartyBig = {}
function TestThirdPartyBig:TestBackward65536()
CheckCompressAndDecompressFile("tests/data/3rdparty/backward65536"
, "all")
end
function TestThirdPartyBig:TestHTML()
CheckCompressAndDecompressFile("tests/data/3rdparty/html"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestPaper100kPdf()
CheckCompressAndDecompressFile("tests/data/3rdparty/paper-100k.pdf"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestGeoProtodata()
CheckCompressAndDecompressFile("tests/data/3rdparty/geo.protodata"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestFireworksJpeg()
CheckCompressAndDecompressFile("tests/data/3rdparty/fireworks.jpeg"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestAsyoulik()
CheckCompressAndDecompressFile("tests/data/3rdparty/asyoulik.txt"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestCompressedRepeated()
CheckCompressAndDecompressFile(
"tests/data/3rdparty/compressed_repeated", {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestAlice29()
CheckCompressAndDecompressFile("tests/data/3rdparty/alice29.txt"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestQuickfox_repeated()
CheckCompressAndDecompressFile("tests/data/3rdparty/quickfox_repeated"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestKppknGtb()
CheckCompressAndDecompressFile("tests/data/3rdparty/kppkn.gtb"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestZeros()
CheckCompressAndDecompressFile("tests/data/3rdparty/zeros"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestMapsdatazrh()
CheckCompressAndDecompressFile("tests/data/3rdparty/mapsdatazrh"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestHtml_x_4()
CheckCompressAndDecompressFile("tests/data/3rdparty/html_x_4"
, {0,1,2,3,4})
end
function TestThirdPartyBig:TestLcet10()
CheckCompressAndDecompressFile("tests/data/3rdparty/lcet10.txt"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestPlrabn12()
CheckCompressAndDecompressFile("tests/data/3rdparty/plrabn12.txt"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:TestUrls10K()
CheckCompressAndDecompressFile("tests/data/3rdparty/urls.10K"
, {0,1,2,3,4,5})
end
function TestThirdPartyBig:Testptt5()
CheckCompressAndDecompressFile("tests/data/3rdparty/ptt5"
, {0,1,2,3,4})
end
function TestThirdPartyBig:TestKennedyXls()
CheckCompressAndDecompressFile("tests/data/3rdparty/kennedy.xls"
, {0,1,2,3,4})
end
TestWoWData = {}
function TestWoWData:TestWarlockWeakAuras()
CheckCompressAndDecompressFile("tests/data/warlockWeakAuras.txt"
, {0,1,2,3,4})
end
function TestWoWData:TestTotalRp3Data()
CheckCompressAndDecompressFile("tests/data/totalrp3.txt"
, {0,1,2,3,4})
end
TestDecompress = {}
-- Test from puff
function TestDecompress:TestStoreEmpty()
CheckDecompressIncludingError("\001\000\000\255\255", "")
end
function TestDecompress:TestStore1()
CheckDecompressIncludingError("\001\001\000\254\255\010", "\010")
end
function TestDecompress:TestStore2()
local t = {}
for i=1, 65535 do
t[i] = "a"
end
local str = table.concat(t)
CheckDecompressIncludingError("\001\255\255\000\000"..str, str)
end
function TestDecompress:TestStore3()
local t = {}
for i=1, 65535 do
t[i] = "a"
end
local str = table.concat(t)
CheckDecompressIncludingError("\000\255\255\000\000"..str
.."\001\255\255\000\000"..str, str..str)
end
function TestDecompress:TestStore4()
-- 0101 00fe ff31
CheckDecompressIncludingError("\001\001\000\254\255\049", "1")
end
function TestDecompress:TestStore5()
local size = 0x5555
local str = GetLimitedRandomString(size)
CheckDecompressIncludingError("\001\085\085\170\170"..str, str)
end
function TestDecompress:TestStoreRandom()
for _ = 1, 20 do
local size = math.random(1, 65535)
local str = GetLimitedRandomString(size)
CheckDecompressIncludingError("\001"..string.char(size%256)
..string.char((size-size%256)/256)
..string.char(255-size%256)
..string.char(255-(size-size%256)/256)..str, str)
end
end
function TestDecompress:TestFix1()
CheckDecompressIncludingError("\003\000", "")
end
function TestDecompress:TestFix2()
CheckDecompressIncludingError("\051\004\000", "1")
end
function TestDecompress:TestFixThenStore1()
local t = {}
for i=1, 65535 do
t[i] = "a"
end
local str = table.concat(t)
CheckDecompressIncludingError("\050\004\000\255\255\000\000"
..str.."\001\255\255\000\000"..str, "1"..str..str)
end
function TestDecompress:TestIncomplete()
-- Additonal 1 byte after the end of compression data
CheckDecompressIncludingError("\001\001\000\254\255\010\000", "\010")
end
function TestDecompress:TestStoreSizeTooBig()
CheckDecompressIncludingError("\001\001\000\254\255", nil)
CheckDecompressIncludingError("\001\002\000\253\255\001", nil)
end
function TestDecompress:TestEmtpy()
CheckDecompressIncludingError("", nil)
end
function TestDecompress:TestOneByte()
for i=0, 255 do
CheckDecompressIncludingError(string.char(i), nil)
end
end
function TestDecompress:TestPuffReturn2()
CheckDecompressIncludingError("\000", nil)
CheckDecompressIncludingError("\002", nil)
CheckDecompressIncludingError("\004", nil)
CheckDecompressIncludingError(HexToString("00 01 00 fe ff"), nil)
CheckDecompressIncludingError(
HexToString("04 80 49 92 24 49 92 24 0f b4 ff ff c3 04"), nil)
end
function TestDecompress:TestPuffReturn245()
CheckDecompressIncludingError(HexToString(
"0c c0 81 00 00 00 00 00 90 ff 6b 04"), nil)
end
function TestDecompress:TestPuffReturn246()
CheckDecompressIncludingError(HexToString("1a 07"), nil)
CheckDecompressIncludingError(HexToString("02 7e ff ff"), nil)
CheckDecompressIncludingError(HexToString(
"04 c0 81 08 00 00 00 00 20 7f eb 0b 00 00"), nil)
end
function TestDecompress:TestPuffReturn247()
CheckDecompressIncludingError(HexToString(
"04 00 24 e9 ff 6d"), nil)
end
function TestDecompress:TestPuffReturn248()
CheckDecompressIncludingError(HexToString(
"04 80 49 92 24 49 92 24 0f b4 ff ff c3 84"), nil)
end
function TestDecompress:TestPuffReturn249()
CheckDecompressIncludingError(HexToString(
"04 80 49 92 24 49 92 24 71 ff ff 93 11 00"), nil)
end
function TestDecompress:TestPuffReturn250()
CheckDecompressIncludingError(HexToString(
"04 00 24 e9 ff ff"), nil)
end
function TestDecompress:TestPuffReturn251()
CheckDecompressIncludingError(HexToString("04 00 24 49"), nil)
end
function TestDecompress:TestPuffReturn252()
CheckDecompressIncludingError(HexToString("04 00 fe ff"), nil)
end
function TestDecompress:TestPuffReturn253()
CheckDecompressIncludingError(HexToString("fc 00 00"), nil)
end
function TestDecompress:TestPuffReturn254()
CheckDecompressIncludingError(HexToString("00 00 00 00 00"), nil)
end
function TestDecompress:TestZlibCoverSupport()
CheckDecompressIncludingError(HexToString("63 00"), nil)
CheckDecompressIncludingError(HexToString("63 18 05"), nil)
CheckDecompressIncludingError(
HexToString("63 18 68 30 d0 0 0"), ("\000"):rep(257))
CheckDecompressIncludingError(HexToString("3 00"), "")
CheckDecompressIncludingError("", nil)
CheckDecompressIncludingError("", nil, true)
end
function TestDecompress:TestZlibCoverWrap()
CheckZlibDecompressIncludingError(
HexToString("77 85"), nil) -- Bad zlib header
CheckZlibDecompressIncludingError(
HexToString("70 85"), nil) -- Bad zlib header
CheckZlibDecompressIncludingError(
HexToString("88 9c"), nil) -- Bad window size
CheckZlibDecompressIncludingError(
HexToString("f8 9c"), nil) -- Bad window size
CheckZlibDecompressIncludingError(
HexToString("78 90"), nil) -- Bad zlib header check
CheckZlibDecompressIncludingError(
HexToString("78 9c 63 00 00 00 01 00 01"), "\000") -- check Adler32
CheckZlibDecompressIncludingError(
HexToString("78 9c 63 00 00 00 01 00"), nil) -- Adler32 incomplete
CheckZlibDecompressIncludingError(
HexToString("78 9c 63 00 00 00 01 00 02"), nil) -- wrong Adler32
CheckZlibDecompressIncludingError(
HexToString("78 9c 63 0"), nil) -- no Adler32
end
function TestDecompress:TestZlibCoverInflate()
CheckDecompressIncludingError(
HexToString("0 0 0 0 0"), nil) -- invalid store block length
CheckDecompressIncludingError(
HexToString("3 0"), "", nil) -- Fixed block
CheckDecompressIncludingError(
HexToString("6"), nil) -- Invalid block type
CheckDecompressIncludingError(
HexToString("1 1 0 fe ff 0"), "\000") -- Stored block
CheckDecompressIncludingError(
HexToString("fc 0 0"), nil) -- Too many length or distance symbols
CheckDecompressIncludingError(
HexToString("4 0 fe ff"), nil) -- Invalid code lengths set
CheckDecompressIncludingError(
HexToString("4 0 24 49 0"), nil) -- Invalid bit length repeat
CheckDecompressIncludingError(
HexToString("4 0 24 e9 ff ff"), nil) -- Invalid bit length repeat
-- Invalid code: missing end of block
CheckDecompressIncludingError(
HexToString("4 0 24 e9 ff 6d"), nil)
-- Invalid literal/lengths set
CheckDecompressIncludingError(
HexToString("4 80 49 92 24 49 92 24 71 ff ff 93 11 0"), nil)
CheckDecompressIncludingError(
HexToString("4 80 49 92 24 49 92 24 71 ff ff 93 11 0"), nil)
-- Invalid distance set
CheckDecompressIncludingError(
HexToString("4 80 49 92 24 49 92 24 f b4 ff ff c3 84"), nil)
-- Invalid literal/length code
CheckDecompressIncludingError(
HexToString("4 c0 81 8 0 0 0 0 20 7f eb b 0 0"), nil)
CheckDecompressIncludingError(
HexToString("2 7e ff ff"), nil) -- Invalid distance code
-- Invalid distance too far
CheckDecompressIncludingError(
HexToString("c c0 81 0 0 0 0 0 90 ff 6b 4 0"), nil)
-- incorrect data check
CheckDecompressIncludingError(
HexToString("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 1"), nil)
-- incorrect length check
CheckDecompressIncludingError(
HexToString("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 1"), nil)
-- pull 17
CheckDecompressIncludingError(
HexToString("5 c0 21 d 0 0 0 80 b0 fe 6d 2f 91 6c"), "")
-- long code
CheckDecompressIncludingError(
HexToString(
"05 e0 81 91 24 cb b2 2c 49 e2 0f 2e 8b 9a 47 56 9f fb fe ec d2 ff 1f")
, "")
-- extra length
CheckDecompressIncludingError(
HexToString("ed c0 1 1 0 0 0 40 20 ff 57 1b 42 2c 4f")
, ("\000"):rep(516))
-- long distance and extra
CheckDecompressIncludingError(
HexToString(
"ed cf c1 b1 2c 47 10 c4 30 fa 6f 35 1d 1 82 59 3d fb be 2e 2a fc f c")
, ("\000"):rep(518))
-- Window end
CheckDecompressIncludingError(
HexToString(
"ed c0 81 0 0 0 0 80 a0 fd a9 17 a9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0")
, nil)
-- inflate_fast TYPE return
CheckDecompressIncludingError(HexToString("2 8 20 80 0 3 0"), "")
-- Window wrap
CheckDecompressIncludingError(HexToString("63 18 5 40 c 0")
, ("\000"):rep(262))
end
function TestDecompress:TestZlibCoverFast()
-- fast length extra bits
CheckDecompressIncludingError(
HexToString(
"e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68")
, nil)
-- fast distance extra bits
CheckDecompressIncludingError(
HexToString(
"25 fd 81 b5 6d 59 b6 6a 49 ea af 35 6 34 eb 8c b9 f6 b9 1e ef 67 49"
, nil))
-- Fast invalid distance code
CheckDecompressIncludingError(HexToString("3 7e 0 0 0 0 0"), nil)
-- Fast literal/length code
CheckDecompressIncludingError(HexToString("1b 7 0 0 0 0 0"), nil)
-- fast 2nd level codes and too far back
CheckDecompressIncludingError(
HexToString(
"d c7 1 ae eb 38 c 4 41 a0 87 72 de df fb 1f b8 36 b1 38 5d ff ff 0")
, nil)
-- Very common case
CheckDecompressIncludingError(
HexToString("63 18 5 8c 10 8 0 0 0 0")
, ("\000"):rep(258)..("\000\001"):rep(4))
-- Continous and wrap aroudn window
CheckDecompressIncludingError(
HexToString("63 60 60 18 c9 0 8 18 18 18 26 c0 28 0 29 0 0 0")
, ("\000"):rep(261)..("\144")..("\000"):rep(6)..("\144\000"))
-- Copy direct from output
CheckDecompressIncludingError(
HexToString("63 0 3 0 0 0 0 0"), ("\000"):rep(6))
end
function TestDecompress:TestAdditionalCoverage()
-- no zlib FLG
CheckZlibDecompressIncludingError(HexToString("78"), nil)
-- Stored block no len
CheckDecompressIncludingError(HexToString("1"), nil)
-- Stored block no len comp
CheckDecompressIncludingError(HexToString("1 1 0"), nil)
-- Stored block not one's complement
CheckDecompressIncludingError(HexToString("1 1 0 ff ff 0"), nil)
-- Stored block not one's complement
CheckDecompressIncludingError(HexToString("1 1 0 fe fe 0"), nil)
CheckDecompressIncludingError(
HexToString("1 34 43 cb bc")..("\000"):rep(17204)
, ("\000"):rep(17204)) -- Stored block
-- Stored block with 1 less byte
CheckDecompressIncludingError(
HexToString("1 34 43 cb bc")..("\000"):rep(17203), nil)
CheckDecompressIncludingError(
HexToString("1 34 43 cb bc")..("\000"):rep(17202), nil)
end
function TestDecompress:Test2ndReturn()
for _ = 1, 10 do
local str = GetLimitedRandomString(math.random(100, 300))
local compressed = LibDeflate:CompressDeflate(str)
local extra_len = math.random(1, 10)
local extra = GetLimitedRandomString(extra_len)
compressed = compressed..extra
local decompressed, unprocessed =
LibDeflate:DecompressDeflate(compressed)
AssertLongStringEqual(str, decompressed)
lu.assertEquals(unprocessed, extra_len)
end
for _ = 1, 10 do
local dict = CreateDictionaryWithoutVerify(
GetLimitedRandomString(math.random(100, 300)))
local str = GetLimitedRandomString(math.random(100, 300))
local compressed = LibDeflate:CompressDeflateWithDict(str, dict)
local extra_len = math.random(1, 10)
local extra = GetLimitedRandomString(extra_len)
compressed = compressed..extra
local decompressed, unprocessed =
LibDeflate:DecompressDeflateWithDict(compressed, dict)
AssertLongStringEqual(str, decompressed)
lu.assertEquals(unprocessed, extra_len)
end
for _ = 1, 10 do
local str = GetLimitedRandomString(math.random(100, 300))
local compressed = LibDeflate:CompressZlib(str)
local extra_len = math.random(1, 10)
local extra = GetLimitedRandomString(extra_len)
compressed = compressed..extra
local decompressed, unprocessed =
LibDeflate:DecompressZlib(compressed)
AssertLongStringEqual(str, decompressed)
lu.assertEquals(unprocessed, extra_len)
end
for _ = 1, 10 do
local dict = CreateDictionaryWithoutVerify(
GetLimitedRandomString(math.random(100, 300)))
local str = GetLimitedRandomString(math.random(100, 300))
local compressed = LibDeflate:CompressZlibWithDict(str, dict)
local extra_len = math.random(1, 10)
local extra = GetLimitedRandomString(extra_len)
compressed = compressed..extra
local decompressed, unprocessed =
LibDeflate:DecompressZlibWithDict(compressed, dict)
AssertLongStringEqual(str, decompressed)
lu.assertEquals(unprocessed, extra_len)
end
end
function TestDecompress:TestDecompressWithDict()
local dict = CreateDictionaryWithoutVerify("abcdefgh")
-- local adler32 = LibDeflate:Adler32("abcdefgh")
-- adler == 0x0e000325
lu.assertEquals(LibDeflate:DecompressZlib(
HexToString("78 9c 63 00 00 00 01 00 01")), "\000")
-- The data needs dictionary, but calling
-- DecompressZlib instead of DecompressZlibWithDict
lu.assertEquals(LibDeflate:DecompressZlib(
HexToString("78 bb 63 00 00 00 01 00 01")), nil)
lu.assertEquals(LibDeflate:DecompressZlib(
HexToString("78 bb 25 03 00 0e 63 00 00 00 01 00 01")), nil)
lu.assertEquals(LibDeflate:DecompressZlibWithDict(
HexToString("78 bb 0e 00 03 25 63 00 00 00 01 00 01"), dict)
, "\000")
-- input ends before dictionary adler32 is read.
lu.assertEquals(LibDeflate:DecompressZlibWithDict(
HexToString("78 bb 0e 00 03 "), dict)
, nil)
lu.assertEquals(LibDeflate:DecompressZlibWithDict(
HexToString("78 bb 0e 00 "), dict)
, nil)
lu.assertEquals(LibDeflate:DecompressZlibWithDict(
HexToString("78 bb 0e "), dict)
, nil)
lu.assertEquals(LibDeflate:DecompressZlibWithDict(
HexToString("78 bb "), dict)
, nil)
-- adler32 does not match
lu.assertEquals(LibDeflate:DecompressZlibWithDict(
HexToString("78 bb 25 03 00 0e 63 00 00 00 01 00 01"), dict)
, nil)
lu.assertEquals(LibDeflate:DecompressZlibWithDict(
HexToString("78 bb 0e 00 03 26 63 00 00 00 01 00 01"), dict)
, nil)
end
TestInternals = {}
-- Test from puff
function TestInternals:TestLoadString()
local LoadStringToTable = LibDeflate.internals.LoadStringToTable
local tmp
for _=1, 50 do
local t = {}
local strlen = math.random(0, 1000)
local str = GetLimitedRandomString(strlen)
local uncorruped_data = {}
for i=1, strlen do
uncorruped_data[i] = math.random(1, 12345)
t[i] = uncorruped_data[i]
end
local start
local stop
if strlen >= 1 then
start = math.random(1, strlen)
stop = math.random(1, strlen)
else
start = 1
stop = 0
end
if start > stop then
tmp = start
start = stop
stop = tmp
end
local offset = math.random(0, strlen)
LoadStringToTable(str, t, start, stop, offset)
for i=-1000, 2000 do
if i < start-offset or i > stop-offset then
lu.assertEquals(t[i], uncorruped_data[i]
, "loadStr corrupts unintended location")
else
lu.assertEquals(t[i], string_byte(str, i+offset)
, ("loadStr gives wrong data!, start=%d, stop=%d, i=%d")
:format(start, stop, i))
end
end
end
end
function TestInternals:TestSimpleRandom()
for _=1, 30 do
local strlen = math.random(0, 1000)
local str = GetLimitedRandomString(strlen)
local level = (math.random() < 0.5) and (math.random(1, 8)) or nil
local expected = str
local configs = {level = level}
local compress = LibDeflate:CompressDeflate(str, configs)
local _, actual = pcall(function() return LibDeflate
:DecompressDeflate(compress) end)
if expected ~= actual then
local strDumpFile = io.open("fail_random.tmp", "wb")
if (strDumpFile) then
strDumpFile:write(str)
print(("Failed test has been dumped to fail_random.tmp,"
.. "with level=%s"):
format(tostring(level)))
strDumpFile:close()
if type(actual) == "string" then
print(("Error msg is:\n"), actual:sub(1, 100))
end
end
lu.assertEquals(false, "My decompress does not match origin.")
end
end
end
function TestInternals:TestAdler32()
lu.assertEquals(LibDeflate:Adler32(""), 1)
lu.assertEquals(LibDeflate:Adler32("1"), 0x00320032)
lu.assertEquals(LibDeflate:Adler32("12"), 0x00960064)
lu.assertEquals(LibDeflate:Adler32("123"), 0x012D0097)
lu.assertEquals(LibDeflate:Adler32("1234"), 0x01F800CB)
lu.assertEquals(LibDeflate:Adler32("12345"), 0x02F80100)
lu.assertEquals(LibDeflate:Adler32("123456"), 0x042E0136)
lu.assertEquals(LibDeflate:Adler32("1234567"), 0x059B016D)
lu.assertEquals(LibDeflate:Adler32("12345678"), 0x074001A5)
lu.assertEquals(LibDeflate:Adler32("123456789"), 0x091E01DE)
lu.assertEquals(LibDeflate:Adler32("1234567890"), 0x0B2C020E)
lu.assertEquals(LibDeflate:Adler32("1234567890a"), 0x0D9B026F)
lu.assertEquals(LibDeflate:Adler32("1234567890ab"), 0x106C02D1)
lu.assertEquals(LibDeflate:Adler32("1234567890abc"), 0x13A00334)
lu.assertEquals(LibDeflate:Adler32("1234567890abcd"), 0x17380398)
lu.assertEquals(LibDeflate:Adler32("1234567890abcde"), 0x1B3503FD)
lu.assertEquals(LibDeflate:Adler32("1234567890abcdef"), 0x1F980463)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefg"), 0x1F9E0466)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefgh"), 0x246C04CE)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefghi"), 0x29A30537)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefghij"), 0x2F4405A1)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefghijk"), 0x3550060C)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefghijkl")
, 0x3BC80678)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefghijklm")
, 0x42AD06E5)
lu.assertEquals(LibDeflate:Adler32("1234567890abcefghijklmn")
, 0x4A000753)
lu.assertEquals(LibDeflate:Adler32(
"1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
, 0x8C40150C)
local adler32Test = GetFileData("tests/data/adler32Test.txt")
lu.assertEquals(LibDeflate:Adler32(adler32Test), 0x5D9BAF5D)
local adler32Test2 = GetFileData("tests/data/adler32Test2.txt")
lu.assertEquals(LibDeflate:Adler32(adler32Test2), 0xD6A07E29)
end
function TestInternals:TestLibStub()
-- Start of LibStub
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2
-- NOTE: It is intended that LibStub is global
LibStub = _G[LIBSTUB_MAJOR]
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
function LibStub:NewLibrary(major, minor)
assert(type(major) ==
"string"
, "Bad argument #2 to `NewLibrary' (string expected)")
minor = assert(tonumber(string.match(minor, "%d+"))
, "Minor version must either be a number or contain a number.")
local oldminor = self.minors[major]
if oldminor and oldminor >= minor then return nil end
self.minors[major], self.libs[major] =
minor, self.libs[major] or {}
return self.libs[major], oldminor
end
function LibStub:GetLibrary(major, silent)
if not self.libs[major] and not silent then
error(("Cannot find a library instance of %q.")
:format(tostring(major)), 2)
end
return self.libs[major], self.minors[major]
end
function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end
-- End of LibStub
local LibStub = _G.LibStub
lu.assertNotNil(LibStub, "LibStub not in global?")
local MAJOR = "LibDeflate"
CheckCompressAndDecompressString("aaabbbcccddddddcccbbbaaa", "all")
lu.assertNotNil(package.loaded["LibDeflate"]
, "LibDeflate is not loaded")
package.loaded["LibDeflate"] = nil
-- Not sure if luaconv can recognize code in dofile()
-- let's just use require
LibDeflate = require("LibDeflate")
lu.assertNotNil(package.loaded["LibDeflate"]
, "LibDeflate is not loaded")
lu.assertNotNil(LibDeflate, "LibStub does not return LibDeflate")
lu.assertEquals(LibStub:GetLibrary(MAJOR, true), LibDeflate
, "Cant find LibDeflate in LibStub.")
CheckCompressAndDecompressString("aaabbbcccddddddcccbbbaaa", "all")
------------------------------------------------------
FullMemoryCollect()
local memory1 = math.floor(collectgarbage("collect")*1024)
lu.assertNotNil(package.loaded["LibDeflate"]
, "LibDeflate is not loaded")
package.loaded["LibDeflate"] = nil
-- Not sure if luaconv can recognize code in dofile()
-- let's just use require
local LibDeflateTmp = require("LibDeflate")
lu.assertNotNil(package.loaded["LibDeflate"]
, "LibDeflate is not loaded")
lu.assertEquals(LibDeflateTmp, LibDeflate
, "LibStub unexpectedly recreates the library.")
lu.assertNotNil(LibDeflate, "LibStub does not return LibDeflate")
lu.assertEquals(LibStub:GetLibrary(MAJOR, true), LibDeflate
, "Cant find LibDeflate in LibStub.")
CheckCompressAndDecompressString("aaabbbcccddddddcccbbbaaa", "all")
FullMemoryCollect()
local memory2 = math.floor(collectgarbage("collect")*1024)
if not _G.jit then
lu.assertTrue((memory2 - memory1 <= 32)
, ("Too much Memory leak after LibStub without update: %d")
:format(memory2-memory1))
end
----------------------------------------------------
LibStub.minors[MAJOR] = -1000
FullMemoryCollect()
local memory3 = math.floor(collectgarbage("collect")*1024)
lu.assertNotNil(package.loaded["LibDeflate"]
, "LibDeflate is not loaded")
package.loaded["LibDeflate"] = nil
-- Not sure if luaconv can recognize code in dofile()
-- let's just use require
LibDeflateTmp = require("LibDeflate")
lu.assertNotNil(package.loaded["LibDeflate"]
, "LibDeflate is not loaded")
CheckCompressAndDecompressString("aaabbbcccddddddcccbbbaaa", "all")
FullMemoryCollect()
local memory4 = math.floor(collectgarbage("collect")*1024)
lu.assertEquals(LibDeflateTmp, LibDeflate
, "LibStub unexpectedly recreates the library.")
lu.assertTrue(LibStub.minors[MAJOR] > -1000
, "LibDeflate is not updated.")
if not _G.jit then
lu.assertTrue((memory4 - memory3 <= 100)
, ("Too much Memory leak after LibStub update: %d")
:format(memory4-memory3))
end
end
function TestInternals:TestByteTo6bitChar()
local _byte_to_6bit_char = LibDeflate.internals._byte_to_6bit_char
lu.assertNotNil(_byte_to_6bit_char)
lu.assertEquals(GetTableSize(_byte_to_6bit_char), 64)
for i= 0, 25 do
lu.assertEquals(_byte_to_6bit_char[i],
string.char(string.byte("a", 1) + i))
end
for i = 26, 51 do
lu.assertEquals(_byte_to_6bit_char[i],
string.char(string.byte("A", 1) + i - 26))
end
for i = 52, 61 do
lu.assertEquals(_byte_to_6bit_char[i],
string.char(string.byte("0", 1) + i - 52))
end
lu.assertEquals(_byte_to_6bit_char[62], "(")
lu.assertEquals(_byte_to_6bit_char[63], ")")
end
function TestInternals:Test6BitToByte()
local _6bit_to_byte = LibDeflate.internals._6bit_to_byte
lu.assertNotNil(_6bit_to_byte)
lu.assertEquals(GetTableSize(_6bit_to_byte), 64)
for i = string.byte("a", 1), string.byte("z", 1) do
lu.assertEquals(_6bit_to_byte[i], i - string.byte("a", 1))
end
for i = string.byte("A", 1), string.byte("Z", 1) do
lu.assertEquals(_6bit_to_byte[i], i - string.byte("A", 1) + 26)
end
for i = string.byte("0", 1), string.byte("9", 1) do
lu.assertEquals(_6bit_to_byte[i], i - string.byte("0", 1) + 52)
end
lu.assertEquals(_6bit_to_byte[string.byte("(", 1)], 62)
lu.assertEquals(_6bit_to_byte[string.byte(")", 1)], 63)
end
TestPresetDict = {}
function TestPresetDict:TestExample()
local dict_str = [[ilvl::::::::110:::1517:3336:3528:3337]]
local dictionary = CreateAndCheckDictionary(dict_str)
local fileData = GetFileData("tests/data/itemStrings.txt")
CheckDictEffectiveness(fileData, dictionary, dict_str)
end
function TestPresetDict:TestEmptyString()
for i=1, 16 do
local dict_str = GetRandomString(i)
local dictionary = CreateAndCheckDictionary(dict_str)
CheckDictEffectiveness("", dictionary, dict_str, true)
end
end
function TestPresetDict:TestCheckDictRandomComplete()
for _ = 1, 10 do
local dict_str = GetRandomStringUniqueChars(
256+math.random(0, 1000))
CreateAndCheckDictionary(dict_str)
end
end
-- Test if last two bytes in the dictionary are hashed, with dict size 3.
function TestPresetDict:TestLength3String1()
for _ = 1, 10 do
local dict_str = GetRandomStringUniqueChars(3)
local dictionary = CreateAndCheckDictionary(dict_str)
local str = dict_str
local compress_dict =
CheckDictEffectiveness(str, dictionary, dict_str)
lu.assertTrue(compress_dict:len() <= 4)
end
end
-- Test if last two bytes in the dictionary is hashed, with dict size 2
function TestPresetDict:TestLength3String2()
for _ = 1, 10 do
local str = GetRandomStringUniqueChars(3)
local dict_str = str:sub(1, 2)
str = str:sub(3, 3)..str
local dictionary = CreateAndCheckDictionary(dict_str)
local compress_dict =
CheckDictEffectiveness(str, dictionary, dict_str)
lu.assertTrue(compress_dict:len() <= 5)
end
end
-- Test if last two bytes in the dictionary is hashed, with dict size 1
function TestPresetDict:TestLength3String3()
for _ = 1, 10 do
local str = GetRandomStringUniqueChars(3)
local dict_str = str:sub(1, 1)
str = str:sub(2, 3)..str
local dictionary = CreateAndCheckDictionary(dict_str)
local compress_dict =
CheckDictEffectiveness(str, dictionary, dict_str)
lu.assertTrue(compress_dict:len() <= 6)
end
end
function TestPresetDict:TestLength257String()
for _ = 1, 10 do
local dict_str = GetRandomStringUniqueChars(257)
local dictionary = CreateAndCheckDictionary(dict_str)
local str = dict_str
local compress_dict =
CheckDictEffectiveness(str, dictionary, dict_str)
lu.assertTrue(compress_dict:len() <= 5)
end
end
function TestPresetDict:TestLength258String()
for _ = 1, 10 do
local dict_str = GetRandomStringUniqueChars(258)
local dictionary = CreateAndCheckDictionary(dict_str)
local str = dict_str
local compress_dict =
CheckDictEffectiveness(str, dictionary, dict_str)
lu.assertTrue(compress_dict:len() <= 4)
end
end
function TestPresetDict:TestLength259String()
for _ = 1, 10 do
local dict_str = GetRandomStringUniqueChars(259)
local dictionary = CreateAndCheckDictionary(dict_str)
local str = dict_str
local compress_dict =
CheckDictEffectiveness(str, dictionary, dict_str)
lu.assertTrue(compress_dict:len() <= 5)
end
end
function TestPresetDict:TestIsEqualAdler32()
local IsEqualAdler32 = LibDeflate.internals.IsEqualAdler32
lu.assertTrue(IsEqualAdler32(4072834167, -222133129))
for _ = 1, 30 do
local rand = math.random(0, 1000)
lu.assertTrue(IsEqualAdler32(rand, rand))
lu.assertTrue(IsEqualAdler32(rand+256*256*256*256, rand))
lu.assertTrue(IsEqualAdler32(rand, rand+256*256*256*256))
lu.assertTrue(IsEqualAdler32(rand-256*256*256*256, rand))
lu.assertTrue(IsEqualAdler32(rand, rand-256*256*256*256))
lu.assertTrue(IsEqualAdler32(rand+256*256*256*256
, rand+256*256*256*256))
end
end
TestEncode = {}
function TestEncode:TestBasic()
CheckEncodeAndDecode("")
for i=0, 255 do
CheckEncodeAndDecode(string_char(i))
end
end
function TestEncode:TestRandom()
for _ = 0, 200 do
local str = GetRandomStringUniqueChars(math.random(256, 1000))
CheckEncodeAndDecode(str)
end
end
-- Bug in LibCompress:GetEncodeTable()
-- version LibCompress Revision 81
-- Date: 2018-02-25 06:31:34 +0000 (Sun, 25 Feb 2018)
function TestEncode:TestLibCompressEncodeBug()
local reservedChars =
"\132\109\114\143\11\32\153\92\230\66\131\127\87\106\89\142\55\228\56"
.."\158\151\53\48\13\71\9\37\208\101\42\217\76\19\250\125\214\146\14"
.."\215\204\249\223\165\45\222\120\161\65\28\144\196\12\43\116\242\179"
.."\194\1\253\147\121\99\3\107\96\67\27\44\100\148\130\221\138\85\129"
.."\166\185\246\239\50\218\94\157\90\81\134\80\175\186\79\122\93\190"
.."\150\154\183\91\152\70\234\169\126\108\251\6\2\22\95\233\180\105"
.."\119\38\229\171\29\192\219\21\241\74\207\159\117\247\72\237\110"
.."\78\118"
local escapedChars = "\145\54"
for _ = 1, 10 do
local str = GetRandomStringUniqueChars(1000)
CheckEncodeAndDecode(str, reservedChars, escapedChars, "")
end
end
function TestEncode:TestRandomComplete1()
for _ = 0, 30 do
local tmp = GetRandomStringUniqueChars(256)
local reserved = tmp:sub(1, 10)
local escaped = tmp:sub(11, 11)
local mapped = tmp:sub(12, 12+math.random(0, 9))
local str = GetRandomStringUniqueChars(math.random(256, 1000))
CheckEncodeAndDecode(str, reserved, escaped, mapped)
end
end
function TestEncode:TestRandomComplete2()
for _ = 0, 30 do
local tmp = GetRandomStringUniqueChars(256)
local reserved = tmp:sub(1, 10)
local escaped = tmp:sub(11, 11)
local str = GetRandomStringUniqueChars(math.random(256, 1000))
CheckEncodeAndDecode(str, reserved, escaped, "")
end
end
function TestEncode:TestRandomComplete3()
for _ = 0, 30 do
local tmp = GetRandomStringUniqueChars(256)
local reserved = tmp:sub(1, 130) -- Over half chractrs escaped
local escaped = tmp:sub(131, 132) -- Two escape char needed.
local mapped = tmp:sub(133, 133+math.random(0, 20))
local str = GetRandomStringUniqueChars(math.random(256, 1000))
CheckEncodeAndDecode(str, reserved, escaped, mapped)
end
end
function TestEncode:TestRandomComplete4()
for _ = 0, 30 do
local tmp = GetRandomStringUniqueChars(256)
local reserved = tmp:sub(1, 130) -- Over half chractrs escaped
local escaped = tmp:sub(131, 132) -- Two escape char needed.
local str = GetRandomStringUniqueChars(math.random(256, 1000))
CheckEncodeAndDecode(str, reserved, escaped, "")
end
end
local function CheckEncodeForPrint(str)
AssertLongStringEqual(LibDeflate:DecodeForPrint(LibDeflate
:EncodeForPrint(str))
, str)
-- test prefixed and trailig control characters or space.
for _, byte in pairs({0, 1, 9, 10, 11, 12, 13, 31, 32, 127}) do
local char = string.char(byte)
AssertLongStringEqual(LibDeflate:DecodeForPrint(LibDeflate
:EncodeForPrint(str)..char)
, str)
AssertLongStringEqual(LibDeflate:DecodeForPrint(LibDeflate
:EncodeForPrint(str)..char..char)
, str)
AssertLongStringEqual(LibDeflate:DecodeForPrint(LibDeflate
:EncodeForPrint(str)..char..char..char)
, str)
AssertLongStringEqual(LibDeflate:DecodeForPrint(char..LibDeflate
:EncodeForPrint(str))
, str)
AssertLongStringEqual(LibDeflate:DecodeForPrint(char..char..
LibDeflate:EncodeForPrint(str))
, str)
end
end
function TestEncode:TestEncodeForPrint()
CheckEncodeForPrint("")
for _ = 1, 100 do
CheckEncodeForPrint(GetRandomStringUniqueChars(
math.random(1, 10)))
end
for i = 0, 255 do
CheckEncodeForPrint(string.char(i))
end
for _ = 1, 400 do
CheckEncodeForPrint(GetRandomStringUniqueChars(
math.random(100, 1000)))
end
local encode_6bit_weakaura =
GetFileData("tests/data/reference/encode_6bit_weakaura.txt")
local decode_6bit_weakaura =
GetFileData("tests/data/reference/decode_6bit_weakaura.txt")
AssertLongStringEqual(LibDeflate:EncodeForPrint(decode_6bit_weakaura)
, encode_6bit_weakaura)
end
function TestEncode:TestDecodeForPrintErrors()
for i = 0, 255 do
if string.char(i):find("[%c ]") then
lu.assertEquals(LibDeflate:DecodeForPrint(string.char(i)), "")
else
lu.assertNil(LibDeflate:DecodeForPrint(string.char(i)))
end
end
for i = 0, 255 do
if not LibDeflate.internals._6bit_to_byte[i] then
lu.assertNil(LibDeflate:DecodeForPrint(("1"
..string.char(i)):rep(100).."1"))
end
end
-- Test multiple string lengths.
for i = 0, 255 do
for reps = 1, 16 do
if not LibDeflate.internals._6bit_to_byte[i] then
lu.assertNil(LibDeflate:DecodeForPrint("2"..(
string.char(i)):rep(reps).."3"))
end
end
end
end
function TestEncode:TestDecodeError()
for _ = 0, 100 do
local tmp = GetRandomStringUniqueChars(256)
local reserved = tmp:sub(1, 10)
local escaped = tmp:sub(11, 11)
local str = GetRandomStringUniqueChars(math.random(256, 1000))
local t = LibDeflate:CreateCodec(reserved, escaped, "")
local encode_funcs = {
{t.Encode, t},
{LibDeflate.EncodeForWoWAddonChannel, LibDeflate},
{LibDeflate.EncodeForWoWChatChannel, LibDeflate},
}
local decode_funcs = {
{t.Decode, t},
{LibDeflate.DecodeForWoWAddonChannel, LibDeflate},
{LibDeflate.DecodeForWoWChatChannel, LibDeflate},
}
local reserved_chars = {
reserved,
"\000",
"sS\000\010\013\124%",
}
for j, func in ipairs(encode_funcs) do
local encoded = func[1](func[2], str)
reserved = reserved_chars[j]
local random = math.random(1, #reserved)
local reserved_char = reserved:sub(random, random)
random = math.random(1, #encoded)
encoded = encoded:sub(1, random-1)
..reserved_char..encoded:sub(random, #encoded)
lu.assertNil(decode_funcs[j][1](decode_funcs[j][2], encoded))
end
end
end
function TestEncode:TestFailCreateCodec()
local t, err
t, err = LibDeflate:CreateCodec("1", "", "2")
lu.assertNil(t)
lu.assertEquals(err, "No escape characters supplied.")
t, err = LibDeflate:CreateCodec("1", "a", "23")
lu.assertNil(t)
lu.assertEquals(err, "The number of reserved characters must be"
.." at least as many as the number of mapped chars.")
t, err = LibDeflate:CreateCodec("", "1", "")
lu.assertNil(t)
lu.assertEquals(err, "No characters to encode.")
t, err = LibDeflate:CreateCodec("1", "2", "1")
lu.assertNil(t)
lu.assertEquals(err, "There must be no duplicate characters in the"
.." concatenation of reserved_chars, escape_chars and"
.." map_chars.")
t, err = LibDeflate:CreateCodec("2", "1", "1")
lu.assertNil(t)
lu.assertEquals(err, "There must be no duplicate characters in the"
.." concatenation of reserved_chars, escape_chars and"
.." map_chars.")
t, err = LibDeflate:CreateCodec("1", "1", "2")
lu.assertNil(t)
lu.assertEquals(err, "There must be no duplicate characters in the"
.." concatenation of reserved_chars, escape_chars and"
.." map_chars.")
local r = {}
for i = 128, 255 do
r[#r+1] = string.char(i)
end
local reserved_chars = "sS\000\010\013\124%"..table_concat(r)
t, err = LibDeflate:CreateCodec(reserved_chars, "\029"
, "\015\020")
lu.assertNil(t)
lu.assertEquals(err, "Out of escape characters.")
t, err = LibDeflate:CreateCodec(reserved_chars, "\029\031"
, "\015\020")
lu.assertIsTable(t)
lu.assertNil(err)
end
TestCompressStrategy = {}
function TestCompressStrategy:TestHtml_x_4Fixed()
CheckCompressAndDecompressFile("tests/data/3rdparty/html_x_4"
, {0,1,3,4}, "fixed")
end
function TestCompressStrategy:TestHtml_x_4HuffmanOnly()
CheckCompressAndDecompressFile("tests/data/3rdparty/html_x_4"
, {0,1,3,4}, "huffman_only")
end
function TestCompressStrategy:TestHtml_x_4Dynamic()
CheckCompressAndDecompressFile("tests/data/3rdparty/html_x_4"
, {0,1,2,3,4}, "dynamic")
end
function TestCompressStrategy:TestAsyoulikFixed()
CheckCompressAndDecompressFile("tests/data/3rdparty/asyoulik.txt"
, {0,1,3,4}, "fixed")
end
function TestCompressStrategy:TestAsyoulikHuffmanOnly()
CheckCompressAndDecompressFile("tests/data/3rdparty/asyoulik.txt"
, {0,1,3,4}, "huffman_only")
end
function TestCompressStrategy:TestAsyoulikDynamic()
CheckCompressAndDecompressFile("tests/data/3rdparty/asyoulik.txt"
, {0,1,3,4}, "dynamic")
end
-- Some hard coded compresses length here.
-- Modify if algorithm changes.
-- (I don't think it will happen in the future though)
function TestCompressStrategy:TestIsFixedStrategyInEffect()
local str = ""
for i=0, 255 do
str = str..string.char(i)
end
for i=255, 0, -1 do
str = str..string.char(i)
end
lu.assertEquals(
LibDeflate:CompressDeflate(str):len(), 517)
lu.assertEquals(
GetFirstBlockType(
LibDeflate:CompressDeflate(str, {strategy = "fixed"}), false)
, 1)
lu.assertEquals(
LibDeflate:CompressDeflate(str, {strategy = "fixed"}):len()
, 542)
lu.assertEquals(
GetFirstBlockType(
LibDeflate:CompressZlib(str, {strategy = "fixed"}, true))
, 1)
lu.assertEquals(
LibDeflate:CompressZlib(str, {strategy = "fixed"}):len()
, 548)
end
function TestCompressStrategy:TestIsHuffmanOnlyStrategyInEffect()
local str = ("a"):rep(1000)
lu.assertEquals(
LibDeflate:CompressDeflate(str):len()
, 10)
lu.assertEquals(
LibDeflate:CompressDeflate(str, {strategy = "huffman_only"}):len()
, 138)
lu.assertEquals(
LibDeflate:CompressZlib(str):len()
,16)
lu.assertEquals(
LibDeflate:CompressZlib(str, {strategy = "huffman_only"}):len()
, 144)
end
function TestCompressStrategy:TestIsDynamicStrategyInEffect()
local str = ""
for i=0, 255 do
str = str..string.char(i)
end
for i=255, 0, -1 do
str = str..string.char(i)
end
lu.assertEquals(
LibDeflate:CompressDeflate(str):len(), 517)
lu.assertEquals(
GetFirstBlockType(
LibDeflate:CompressDeflate(str, {strategy = "dynamic"}), false)
, 2)
lu.assertEquals(
LibDeflate:CompressDeflate(str, {strategy = "dynamic"}):len()
, 536)
lu.assertEquals(
GetFirstBlockType(
LibDeflate:CompressZlib(str, {strategy = "dynamic"}, true))
, 2)
lu.assertEquals(
LibDeflate:CompressZlib(str, {strategy = "dynamic"}):len()
, 542)
end
TestErrors = {}
local function TestCorruptedDictionary(msg_prefix, func, dict)
-- Test corrupted dictionary
local backup = DeepCopy(dict)
for i = 1, 100 do
if i == 1 then
dict = nil
elseif i == 2 then
dict.string_table = 1
elseif i == 3 then
dict.string_table = nil
elseif i == 4 then
dict.strlen = {}
elseif i == 5 then
dict.strlen = 32769
elseif i == 6 then
dict.string_table[#dict.string_table+1] = 97
elseif i == 7 then
dict.hash_tables = 1
elseif i == 8 then
dict.hash_tables = nil
elseif i == 9 then
dict.adler32 = nil
else
break
end
if i == 1 then
lu.assertErrorMsgContains(
msg_prefix
.."'dictionary' - table expected got 'nil'."
, function() return func(dict) end)
else
lu.assertErrorMsgContains(
msg_prefix
.."'dictionary' - corrupted dictionary."
, function() return func(dict) end)
end
dict = backup
backup = DeepCopy(dict)
func(dict)
end
end
-- arguments to "func": str, dictionary, configs
local function TestInvalidCompressDecompressArgs(msg_prefix, func
, check_dictionary, check_configs)
lu.assertErrorMsgContains(
msg_prefix
.."'str' - string expected got 'nil'."
, function() return func() end)
lu.assertErrorMsgContains(
msg_prefix
.."'str' - string expected got 'table'."
, function() return func({}) end)
local str = GetRandomString(0, 5)
local dict = CreateDictionaryWithoutVerify(
GetRandomString(math.random(1, 32768)))
if check_dictionary then
TestCorruptedDictionary(msg_prefix,
function(dict2) return func(str, dict2, {}) end, dict)
else
func(str, nil, {})
end
if check_configs then
func(str, dict, nil)
func(str, dict, {})
lu.assertErrorMsgContains(
(
msg_prefix
.."'configs' - nil or table expected got '%s'.")
:format(type(1))
, function() return func(str, dict, 1) end)
for i = 0, 9 do
func(str, dict, {level = i})
end
local strategies = {"fixed", "huffman_only", "dynamic"}
for _, strategy in ipairs(strategies) do
func(str, dict, {strategy = strategy})
func(str, dict, {level = math.random(0, 9) -- NOTE: here
, strategy = strategy})
end
lu.assertErrorMsgContains(
msg_prefix
.."'configs' - unsupported table key in the configs:"
.." 'not_a_key'."
, function() return func(str, dict, {not_a_key=1}) end)
lu.assertErrorMsgContains(
msg_prefix
.."'configs' - unsupported 'level': 10."
, function() return func(str, dict, {level=10}) end)
lu.assertErrorMsgContains(
msg_prefix
.."'configs' - unsupported 'strategy': 'dne'."
, function() return func(str, dict, {strategy="dne"}) end)
else
func(str, dict, 1)
end
end
function TestErrors:TestAdler32()
lu.assertErrorMsgContains("Usage: LibDeflate:Adler32(str): 'str'"
.." - string expected got 'nil'."
, function() LibDeflate:Adler32() end)
lu.assertErrorMsgContains("Usage: LibDeflate:Adler32(str): 'str'"
.." - string expected got 'table'."
, function() LibDeflate:Adler32({}) end)
LibDeflate:Adler32("") -- No error
end
function TestErrors:TestCreateDictionary()
LibDeflate:CreateDictionary("1", 1, 0x00320032)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'str' - string expected got 'nil'."
, function() LibDeflate:CreateDictionary(nil, 1, 0x00320032) end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'str' - string expected got 'table'."
, function() LibDeflate:CreateDictionary({}, 1, 0x00320032) end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'strlen' - number expected got 'nil'."
, function() LibDeflate:CreateDictionary("1", nil, 0x00320032) end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'adler32' - number expected got 'nil'."
, function() LibDeflate:CreateDictionary("1", 1, nil) end)
lu.assertEquals(LibDeflate:Adler32(""), 1)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'str' - Empty string is not allowed."
, function() LibDeflate:CreateDictionary("", 0, 1) end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'str' - string longer than 32768 bytes is not allowed."
.." Got 32769 bytes."
, function() LibDeflate:CreateDictionary(("\000"):rep(32769)
, 32769, LibDeflate:Adler32(("\000"):rep(32769))) end)
-- ^ Dont calculate Adler32 in run-time in real problem plz.
LibDeflate:CreateDictionary(("\000"):rep(32768)
, 32768, LibDeflate:Adler32(("\000"):rep(32768)))
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'strlen' does not match the actual length of 'str'."
.." 'strlen': 32767, '#str': 32768 ."
.." Please check if 'str' is modified unintentionally."
, function() LibDeflate:CreateDictionary(("\000"):rep(32768)
, 32767, LibDeflate:Adler32(("\000"):rep(32768))) end)
-- ^ Dont calculate Adler32 in run-time in real problem plz.
lu.assertErrorMsgContains(
("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):"
.." 'adler32' does not match the actual adler32 of 'str'."
.." 'adler32': %u, 'Adler32(str)': %u ."
.." Please check if 'str' is modified unintentionally.")
:format(LibDeflate:Adler32(("\000"):rep(32768))+1
, LibDeflate:Adler32(("\000"):rep(32768)))
, function() LibDeflate:CreateDictionary(("\000"):rep(32768)
, 32768, LibDeflate:Adler32(("\000"):rep(32768))+1) end)
end
function TestErrors:TestCompressDeflate()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:CompressDeflate(str, configs): "
, function(str, _, configs)
return LibDeflate:CompressDeflate(str, configs) end
, false, true)
end
function TestErrors:TestCompressDeflateWithDict()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:CompressDeflateWithDict"
.."(str, dictionary, configs): "
, function(str, dictionary, configs)
return LibDeflate:
CompressDeflateWithDict(str, dictionary, configs) end
, true, true)
end
function TestErrors:TestCompressZlib()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:CompressZlib(str, configs): "
, function(str, _, configs)
return LibDeflate:CompressZlib(str, configs) end
, false, true)
end
function TestErrors:TestCompressZlibWithDict()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:CompressZlibWithDict"
.."(str, dictionary, configs): "
, function(str, dictionary, configs)
return LibDeflate:
CompressZlibWithDict(str, dictionary, configs) end
, true, true)
end
function TestErrors:TestDecompressDeflate()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:DecompressDeflate(str): "
, function(str, _, _)
return LibDeflate:DecompressDeflate(str) end
, false, false)
end
function TestErrors:TestDecompressZlib()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:DecompressZlib(str): "
, function(str, _, _)
return LibDeflate:DecompressZlib(str) end
, false, false)
end
function TestErrors:TestDecompressDeflateWithDict()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:DecompressDeflateWithDict(str, dictionary): "
, function(str, dict, _)
return LibDeflate:DecompressDeflateWithDict(str, dict) end
, true, false)
end
function TestErrors:TestDecompressZlibWithDict()
TestInvalidCompressDecompressArgs(
"Usage: LibDeflate:DecompressZlibWithDict(str, dictionary): "
, function(str, dict, _)
return LibDeflate:DecompressZlibWithDict(str, dict) end
, true, false)
end
function TestErrors:TestCreateCodec()
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateCodec(reserved_chars,"
.." escape_chars, map_chars):"
.." All arguments must be string."
, function()
LibDeflate:CreateCodec(nil, "", "")
end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateCodec(reserved_chars,"
.." escape_chars, map_chars):"
.." All arguments must be string."
, function()
LibDeflate:CreateCodec("", nil, "")
end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:CreateCodec(reserved_chars,"
.." escape_chars, map_chars):"
.." All arguments must be string."
, function()
LibDeflate:CreateCodec("", "", nil)
end)
local t, err = LibDeflate:CreateCodec("1", "2", "")
lu.assertNil(err)
end
function TestErrors:TestEncodeDecode()
local codec = LibDeflate:CreateCodec("\000", "\001", "")
lu.assertErrorMsgContains(
"Usage: codec:Encode(str):"
.." 'str' - string expected got 'nil'."
, function() codec:Encode() end)
lu.assertErrorMsgContains(
"Usage: codec:Decode(str):"
.." 'str' - string expected got 'nil'."
, function() codec:Decode() end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:EncodeForWoWAddonChannel(str):"
.." 'str' - string expected got 'nil'."
, function() LibDeflate:EncodeForWoWAddonChannel() end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:DecodeForWoWAddonChannel(str):"
.." 'str' - string expected got 'nil'."
, function() LibDeflate:DecodeForWoWAddonChannel() end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:EncodeForWoWChatChannel(str):"
.." 'str' - string expected got 'nil'."
, function() LibDeflate:EncodeForWoWChatChannel() end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:DecodeForWoWChatChannel(str):"
.." 'str' - string expected got 'nil'."
, function() LibDeflate:DecodeForWoWChatChannel() end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:EncodeForPrint(str):"
.." 'str' - string expected got 'nil'."
, function() LibDeflate:EncodeForPrint() end)
lu.assertErrorMsgContains(
"Usage: LibDeflate:DecodeForPrint(str):"
.." 'str' - string expected got 'nil'."
, function() LibDeflate:DecodeForPrint() end)
end
local lua_program = "lua"
local function RunCommandline(args, stdin)
local input_filename = "tests/test.stdin"
if stdin then
WriteToFile(input_filename, stdin)
else
WriteToFile(input_filename, "")
end
local stdout_filename = "tests/test.stderr"
local stderr_filename = "tests/test.stdout"
local libdeflate_file = "./LibDeflate.lua"
if os.getenv("OS") and os.getenv("OS"):find("Windows") then
libdeflate_file = "LibDeflate.lua"
end
local status, _, ret = os.execute(lua_program.." "..libdeflate_file.." "
..args.." >"..input_filename
.. "> "..stdout_filename.." 2> "..stderr_filename)
local returned_status
if type(status) == "number" then -- lua 5.1
returned_status = status
else -- Lua 5.2/5.3
returned_status = ret
if not status and ret == 0 then
returned_status = -1
-- Lua bug on Windows when the returned value is -1, ret is 0
end
end
local stdout = GetFileData(stdout_filename)
local stderr = GetFileData(stderr_filename)
return returned_status, stdout, stderr
end
TestCommandLine = {}
function TestCommandLine:TestHelp()
local returned_status, stdout, stderr = RunCommandline("-h")
lu.assertEquals(returned_status, 0)
local str = LibDeflate._COPYRIGHT
.."\nUsage: lua LibDeflate.lua [OPTION] [INPUT] [OUTPUT]\n"
.." -0 store only. no compression.\n"
.." -1 fastest compression.\n"
.." -9 slowest and best compression.\n"
.." -d do decompression instead of compression.\n"
.." --dict <filename> specify the file that contains"
.." the entire preset dictionary.\n"
.." -h give this help.\n"
.." --strategy <fixed/huffman_only/dynamic>"
.." specify a special compression strategy.\n"
.." -v print the version and copyright info.\n"
.." --zlib use zlib format instead of raw deflate.\n"
if stdout:find(str, 1, true) then
lu.assertStrContains(stdout, str)
else
str = str:gsub("\n", "\r\n")
lu.assertStrContains(stdout, str)
end
lu.assertEquals(stderr, "")
end
function TestCommandLine:TestCopyright()
local returned_status, stdout, stderr = RunCommandline("-v")
lu.assertEquals(returned_status, 0)
local str = LibDeflate._COPYRIGHT
if stdout:find(str, 1, true) then
lu.assertStrContains(stdout, str)
else
str = str:gsub("\n", "\r\n")
lu.assertStrContains(stdout, str)
end
lu.assertEquals(stderr, "")
end
function TestCommandLine:TestErrors()
local returned_status, stdout, stderr
returned_status, stdout, stderr =
RunCommandline("-invalid")
lu.assertNotEquals(returned_status, 0)
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr, ("LibDeflate: Invalid argument: %s")
:format("-invalid"))
returned_status, stdout, stderr =
RunCommandline("tests/data/reference/item_strings.txt --dict")
lu.assertNotEquals(returned_status, 0)
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr, "You must speicify the dict filename")
returned_status, stdout, stderr =
RunCommandline("tests/data/reference/item_strings.txt --dict DNE")
lu.assertNotEquals(returned_status, 0)
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr,
("LibDeflate: Cannot read the dictionary file '%s':")
:format("DNE"))
returned_status, stdout, stderr =
RunCommandline("DNE DNE")
lu.assertNotEquals(returned_status, 0)
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr, "LibDeflate: Cannot read the file 'DNE':")
returned_status, stdout, stderr =
RunCommandline("tests/data/reference/item_strings.txt ..")
lu.assertNotEquals(returned_status, 0)
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr, "LibDeflate: Cannot write the file '..':")
returned_status, stdout, stderr =
RunCommandline("tests/data/reference/item_strings.txt")
lu.assertNotEquals(returned_status, 0)
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr, "LibDeflate:"
.." You must specify both input and output files.")
returned_status, stdout, stderr =
RunCommandline("-d tests/data/reference/item_strings.txt"
.." tests/test_commandline.tmp")
lu.assertNotEquals(returned_status, 0)
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr, "LibDeflate: Decompress fails.")
end
function TestCommandLine:TestCompressAndDecompress()
local funcs = {"CompressDeflate", "CompressDeflateWithDict"
, "CompressZlib", "CompressZlibWithDict"
, "DecompressDeflate", "DecompressDeflateWithDict"
, "DecompressZlib", "DecompressZlibWithDict"}
local args = {"", "--dict tests/dictionary32768.txt"
, "--zlib", "--zlib --dict tests/dictionary32768.txt"
, "-d", "-d --dict tests/dictionary32768.txt"
, "-d --zlib", "-d --zlib --dict tests/dictionary32768.txt"}
local inputs = {"tests/data/reference/item_strings.txt"
,"tests/data/reference/item_strings.txt"
, "tests/data/reference/item_strings.txt"
, "tests/data/reference/item_strings.txt"
, "tests/data/reference/item_strings_deflate.txt"
, "tests/data/reference/item_strings_deflate_with_dict.txt"
, "tests/data/reference/item_strings_zlib.txt"
, "tests/data/reference/item_strings_zlib_with_dict.txt"}
local addition_args = {
"-0 "
, "-1 --strategy huffman_only"
, "-5 --strategy dynamic"
, "-9 --strategy fixed"
, ""
}
local addition_configs = {
{level = 0}
, {level = 1, strategy = "huffman_only"}
, {level = 5, strategy = "dynamic"}
, {level = 9, strategy = "fixed"}
, nil
}
for k, func_name in ipairs(funcs) do
local configs
local addition_arg
for i = 1, #addition_args do
configs = addition_configs[i]
addition_arg = addition_args[i]
if not configs then
print(("Testing TestCommandline: %s")
:format(func_name))
else
print(
("Testing TestCommandline: %s level: %s strategy: %s")
:format(func_name, tostring(configs.level)
, tostring(configs.strategy)))
end
local returned_status, stdout, stderr =
RunCommandline(args[k].." "..addition_arg
.." "..inputs[k]
.." tests/test_commandline.tmp")
lu.assertEquals(stdout, "")
lu.assertStrContains(stderr, ("Successfully writes %d bytes")
:format(GetFileData("tests/test_commandline.tmp"):len()))
lu.assertEquals(returned_status, 0)
local result
if func_name:find("Dict") then
result = LibDeflate[func_name](LibDeflate, GetFileData(
inputs[k]), dictionary32768, configs)
else
result = LibDeflate[func_name](LibDeflate, GetFileData(
inputs[k]), configs)
end
lu.assertNotNil(result)
lu.assertEquals(GetFileData("tests/test_commandline.tmp")
, result)
end
end
end
TestCompressRatio = {}
-- May need to modify number if algorithm changes.
function TestCompressRatio:TestSmallTest()
-- avoid github auto CRLF problem by removing \n in the file.
local fileData = GetFileData("tests/data/smalltest_no_newline.txt")
lu.assertEquals(fileData:len(), 28453)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=0}):len()
<= 28458)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=1}):len()
<= 7467)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=2}):len()
<= 7011)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=3}):len()
<= 6740)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=4}):len()
<= 6401)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=5}):len()
<= 5992)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=6}):len()
<= 5884)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=7}):len()
<= 5829)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=8}):len()
<= 5820)
lu.assertTrue(LibDeflate:CompressDeflate(fileData, {level=9}):len()
<= 5820)
end
TestExported = {}
function TestExported:TestExported()
local exported = {
EncodeForWoWChatChannel = "function",
_COPYRIGHT = "string",
DecodeForWoWAddonChannel = "function",
CompressDeflate = "function",
DecompressDeflate = "function",
CompressDeflateWithDict = "function",
DecompressZlibWithDict = "function",
CreateCodec = "function",
DecodeForWoWChatChannel = "function",
internals = "table",
_VERSION = "string",
_MAJOR = "string",
_MINOR = "number",
Adler32 = "function",
CreateDictionary = "function",
CompressZlibWithDict = "function",
EncodeForPrint = "function",
CompressZlib = "function",
DecodeForPrint = "function",
DecompressDeflateWithDict = "function",
EncodeForWoWAddonChannel = "function",
DecompressZlib = "function",
}
for k, v in pairs(exported) do
lu.assertEquals(v, type(LibDeflate[k]))
end
for k, v in pairs(LibDeflate) do
lu.assertEquals(type(v), exported[k])
end
end
--------------------------------------------------------------
-- Coverage Tests --------------------------------------------
--------------------------------------------------------------
local function AddToCoverageTest(suite, test)
assert(suite)
assert(type(suite[test]) == "function")
CodeCoverage[test] = function(_, ...)
return suite[test](_G[suite], ...) end
end
local function AddAllToCoverageTest(suite)
for k, _ in pairs(suite) do
AddToCoverageTest(suite, k)
end
end
-- Run "luajit -lluacov tests/Test.lua CodeCoverage" for test coverage test.
CodeCoverage = {}
AddAllToCoverageTest(TestBasicStrings)
AddAllToCoverageTest(TestDecompress)
AddAllToCoverageTest(TestInternals)
AddAllToCoverageTest(TestPresetDict)
AddAllToCoverageTest(TestEncode)
AddAllToCoverageTest(TestErrors)
AddToCoverageTest(TestMyData, "TestSmallTest")
AddToCoverageTest(TestThirdPartyBig, "Testptt5")
AddToCoverageTest(TestThirdPartyBig, "TestGeoProtodata")
AddToCoverageTest(TestCompressStrategy, "TestIsFixedStrategyInEffect")
AddToCoverageTest(TestCompressStrategy, "TestIsDynamicStrategyInEffect")
AddToCoverageTest(TestCompressStrategy, "TestIsHuffmanOnlyStrategyInEffect")
-- Run "lua tests/Test.lua CommandLineCodeCoverage "
-- for test coverage test and CommandLineCodeCoverage
-- DONT run with "luajit -lluaconv"
CommandLineCodeCoverage = {}
for k, v in pairs(TestCommandLine) do
CommandLineCodeCoverage[k] = function(_, ...)
lua_program = "lua -lluacov"
return TestCommandLine[k](TestCommandLine, ...)
end
end
-- Check if decompress will produce any lua error for random string.
-- Expectation is that no Lua error.
-- This test is not run in CI.
DecompressLuaErrorTest = {}
function DecompressLuaErrorTest:Test()
math.randomseed(os.time())
for _=1, 10000 do
local len = math.random(0, 10000)
local str = GetRandomString(len)
local dict = CreateDictionaryWithoutVerify(
GetRandomString(math.random(1, 32768)))
local r1, r2
r1, r2 = LibDeflate:DecompressDeflate(str)
-- Check the type of return value
assert((type(r1) == "string" or type(r1) == "nil") and r2 % 1 == 0)
r1, r2 = LibDeflate:DecompressZlib(str)
assert((type(r1) == "string" or type(r1) == "nil") and r2 % 1 == 0)
r1, r2 = LibDeflate:DecompressDeflateWithDict(str, dict)
assert((type(r1) == "string" or type(r1) == "nil") and r2 % 1 == 0)
r1, r2 = LibDeflate:DecompressZlibWithDict(str, dict)
assert((type(r1) == "string" or type(r1) == "nil") and r2 % 1 == 0)
print("Decompressed one random string without Lua error.")
print(StringForPrint(StringToHex(str)))
end
end
-- Tests for some huge test data.
-- The test data is not in the repository.
-- Run the batch script in tests\dev_scripts\download_huge_data.bat
-- to download.
-- This test is not run in CI.
HugeTests = {}
function HugeTests:TestCanterburyBible()
CheckCompressAndDecompressFile("tests/huge_data/bible.txt", "all")
end
function HugeTests:TestCanterburyEColi()
CheckCompressAndDecompressFile("tests/huge_data/E.coli", "all")
end
function HugeTests:TestCanterburyWorld129()
CheckCompressAndDecompressFile("tests/huge_data/world192.txt", "all")
end
do
local silesia_files = {"dickens", "mozilla", "mr", "nci", "ooffice"
, "osdb", "reymont", "samba", "sao", "webster", "xml", "x-ray"}
for _, f in pairs(silesia_files) do
HugeTests["TestSilesia"..f:sub(1, 1):upper()..f:sub(2)] = function()
CheckCompressAndDecompressFile("tests/huge_data/"..f
, {0, 1, 2, 3, 4})
end
end
end
for k, v in pairs(_G) do
if type(k) == "string" and (k:find("^Test") or k:find("^test")) then
assert(type(v) == "table", "Globals start with Test or test"
.." must be table: "..k)
for kk, vv in pairs(v) do
assert(type(kk) == "string"
and kk:find("^Test"), "All members in test table"
.." s key must start with Test: "..tostring(kk))
assert(type(vv) == "function", "All members in test table"
.." must be function")
end
end
end
--
-- Performance Evaluation, compared with LibCompress
--
local function CheckCompressAndDecompressLibCompress(
string_or_filename, is_file)
FullMemoryCollect()
local LibCompress = require("LibCompress")
local origin
if is_file then
origin = GetFileData(string_or_filename)
else
origin = string_or_filename
end
FullMemoryCollect()
local total_memory_before = math.floor(collectgarbage("count")*1024)
do
print(
(">>>>> %s: %s size: %d B (LibCompress)")
:format(is_file and "File" or "String",
string_or_filename:sub(1, 40), origin:len()
))
local compress_to_run = {
{"Compress", origin},
{"CompressLZW", origin},
{"CompressHuffman", origin},
}
for j, compress_running in ipairs(compress_to_run) do
-- Compress by raw deflate
local compress_func_name = compress_running[1]
local compress_memory_leaked, compress_memory_used
, compress_time, compress_data =
MemCheckAndBenchmarkFunc(LibCompress
, unpack(compress_running))
local decompress_to_run = {
{"Decompress", compress_data},
{"Decompress", compress_data},
{"Decompress", compress_data},
}
lu.assertEquals(#decompress_to_run, #compress_to_run)
-- Try decompress by LibDeflate
local decompress_memory_leaked, decompress_memory_used,
decompress_time, decompress_data =
MemCheckAndBenchmarkFunc(LibCompress
, unpack(decompress_to_run[j]))
AssertLongStringEqual(decompress_data, origin
, compress_func_name
.." LibCompress decompress result not match origin string.")
print(
("%s: Size : %d B,Time: %.3f ms, "
.."Speed: %.0f KB/s, Memory: %d B,"
.." Mem/input: %.2f, (memleak?: %d B)\n")
:format(compress_func_name
, compress_data:len(), compress_time
, compress_data:len()/compress_time
, compress_memory_used
, compress_memory_used/origin:len()
, compress_memory_leaked
),
("%s: cRatio: %.2f,Time: %.3f ms"
..", Speed: %.0f KB/s, Memory: %d B,"
.." Mem/input: %.2f, (memleak?: %d B)"):format(
decompress_to_run[j][1]
, origin:len()/compress_data:len(), decompress_time
, decompress_data:len()/decompress_time
, decompress_memory_used
, decompress_memory_used/origin:len()
, decompress_memory_leaked
)
)
print("")
end
end
FullMemoryCollect()
local total_memory_after = math.floor(collectgarbage("count")*1024)
local total_memory_difference = total_memory_before - total_memory_after
if total_memory_difference > 0 then
local ignore_leak = " (Ignore when the test is for LibCompress)"
print(
(">>>>> %s: %s size: %d B\n")
:format(is_file and "File" or "String"
, string_or_filename:sub(1, 40), origin:len()),
("Actual Memory Leak in the test: %d"..ignore_leak.."\n")
:format(total_memory_difference),
"\n")
end
end
local function EvaluatePerformance(filename)
local interpreter = _G._VERSION
if _G.jit then
interpreter = interpreter.."(LuaJIT)"
end
print(interpreter)
print("^^^^^^^^^^^^")
CheckCompressAndDecompressLibCompress(filename, true)
CheckCompressAndDecompressFile(filename, "all")
end
PerformanceEvaluation = {}
function PerformanceEvaluation:TestEvaluateWarlockWeakAuras()
EvaluatePerformance("tests/data/warlockWeakAuras.txt")
end
function PerformanceEvaluation:TestEvaluateTotalRp3Data()
EvaluatePerformance("tests/data/totalrp3.txt")
end
local runner = lu.LuaUnit.new()
local exitCode = runner:runSuite()
print("========================================================")
print("LibDeflate", "Version:", LibDeflate._VERSION, "\n")
print("Exported keys:")
for k, v in pairs(LibDeflate) do
assert(type(k) == "string")
print(k, type(v))
end
print("--------------------------------------------------------")
if exitCode == 0 then
print("TEST OK")
else
print("TEST FAILED")
end
os.exit(exitCode)