Files
BigChef/processor/luahelper-test.lua

492 lines
22 KiB
Lua

-- Load the helper script
dofile("luahelper.lua")
-- Test helper function
local function assert(condition, message)
if not condition then error("ASSERTION FAILED: " .. (message or "unknown error")) end
end
local function test(name, fn)
local ok, err = pcall(fn)
if ok then
print("PASS: " .. name)
else
print("FAIL: " .. name .. " - " .. tostring(err))
end
end
-- Test fromCSV option validation
test("fromCSV invalid option", function()
local csv = "a,b,c\n1,2,3"
local rows, err = fromCSV(csv, { invalidOption = true })
assert(rows ~= nil and #rows == 0, "Should return empty table on error")
assert(err ~= nil, "Should return error message")
assert(string.find(err, "unknown option"), "Error should mention unknown option")
end)
-- Test toCSV error handling
test("toCSV invalid delimiter", function()
local rows = { { "a", "b", "c" } }
local csv, err = toCSV(rows, { delimiter = 123 })
-- toCSV converts delimiter to string, so 123 becomes "123"
assert(csv == "a123b123c", "Should convert delimiter to string")
assert(err == nil, "Should not return error")
end)
-- Test fromCSV basic parsing
test("fromCSV basic", function()
local csv = "a,b,c\n1,2,3\n4,5,6"
local rows, err = fromCSV(csv)
if err then error("fromCSV error: " .. err) end
assert(#rows == 3, "Should have 3 rows")
assert(rows[1][1] == "a", "First row first field should be 'a'")
assert(rows[2][2] == "2", "Second row second field should be '2'")
end)
-- Test fromCSV with headers
test("fromCSV with headers", function()
local csv = "foo,bar,baz\n1,2,3\n4,5,6"
local rows, err = fromCSV(csv, { hasheader = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 data rows")
assert(rows[1][1] == "1", "First row first field should be '1'")
assert(rows[1].foo == "1", "First row foo should be '1'")
assert(rows[1].bar == "2", "First row bar should be '2'")
assert(rows[1].baz == "3", "First row baz should be '3'")
end)
-- Test fromCSV with custom delimiter
test("fromCSV with tab delimiter", function()
local csv = "a\tb\tc\n1\t2\t3"
local rows, err = fromCSV(csv, { delimiter = "\t" })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows")
assert(rows[1][1] == "a", "First row first field should be 'a'")
assert(rows[2][2] == "2", "Second row second field should be '2'")
end)
-- Test fromCSV with quoted fields
test("fromCSV with quoted fields", function()
local csv = '"hello,world","test"\n"foo","bar"'
local rows, err = fromCSV(csv)
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows")
assert(rows[1][1] == "hello,world", "Quoted field with comma should be preserved")
assert(rows[1][2] == "test", "Second field should be 'test'")
end)
-- Test toCSV basic
test("toCSV basic", function()
local rows = { { "a", "b", "c" }, { "1", "2", "3" } }
local csv, err = toCSV(rows)
if err then error("toCSV error: " .. err) end
assert(csv == "a,b,c\n1,2,3", "CSV output should match expected")
end)
-- Test toCSV with custom delimiter
test("toCSV with tab delimiter", function()
local rows = { { "a", "b", "c" }, { "1", "2", "3" } }
local csv, err = toCSV(rows, { delimiter = "\t" })
if err then error("toCSV error: " .. err) end
assert(csv == "a\tb\tc\n1\t2\t3", "TSV output should match expected")
end)
-- Test toCSV with fields needing quoting
test("toCSV with quoted fields", function()
local rows = { { "hello,world", "test" }, { "foo", "bar" } }
local csv, err = toCSV(rows)
if err then error("toCSV error: " .. err) end
assert(csv == '"hello,world",test\nfoo,bar', "Fields with commas should be quoted")
end)
-- Test round trip
test("fromCSV toCSV round trip", function()
local original = "a,b,c\n1,2,3\n4,5,6"
local rows, err = fromCSV(original)
if err then error("fromCSV error: " .. err) end
local csv, err = toCSV(rows)
if err then error("toCSV error: " .. err) end
assert(csv == original, "Round trip should preserve original")
end)
-- Test round trip with headers
test("fromCSV toCSV round trip with headers", function()
local original = "foo,bar,baz\n1,2,3\n4,5,6"
local rows, err = fromCSV(original, { hasheader = true })
if err then error("fromCSV error: " .. err) end
local csv, err = toCSV(rows)
if err then error("toCSV error: " .. err) end
local expected = "1,2,3\n4,5,6"
assert(csv == expected, "Round trip with headers should preserve data rows")
end)
-- Test fromCSV with comments
test("fromCSV with comments", function()
local csv = "# This is a comment\nfoo,bar,baz\n1,2,3\n# Another comment\n4,5,6"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 3, "Should have 3 rows (comments filtered, header + 2 data rows)")
assert(rows[1][1] == "foo", "First row should be header row")
assert(rows[2][1] == "1", "Second row first field should be '1'")
assert(rows[3][1] == "4", "Third row first field should be '4'")
end)
-- Test fromCSV with comments and headers
test("fromCSV with comments and headers", function()
local csv = "#mercenary_profiles\nId,Name,Value\n1,Test,100\n# End of data\n2,Test2,200"
local rows, err = fromCSV(csv, { hasheader = true, hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 data rows")
assert(rows[1].Id == "1", "First row Id should be '1'")
assert(rows[1].Name == "Test", "First row Name should be 'Test'")
assert(rows[1].Value == "100", "First row Value should be '100'")
assert(rows[2].Id == "2", "Second row Id should be '2'")
end)
-- Test fromCSV with comments disabled
test("fromCSV without comments", function()
local csv = "# This should not be filtered\nfoo,bar\n1,2"
local rows, err = fromCSV(csv, { hascomments = false })
if err then error("fromCSV error: " .. err) end
assert(#rows == 3, "Should have 3 rows (including comment)")
assert(rows[1][1] == "# This should not be filtered", "Comment line should be preserved")
end)
-- Test fromCSV with comment at start
test("fromCSV comment at start", function()
local csv = "# Header comment\nId,Name\n1,Test"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows (comment filtered)")
assert(rows[1][1] == "Id", "First row should be header")
end)
-- Test fromCSV with comment with leading whitespace
test("fromCSV comment with whitespace", function()
local csv = " # Comment with spaces\nId,Name\n1,Test"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows (comment with spaces filtered)")
assert(rows[1][1] == "Id", "First row should be header")
end)
-- Test fromCSV with comment with tabs
test("fromCSV comment with tabs", function()
local csv = "\t# Comment with tab\nId,Name\n1,Test"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows (comment with tab filtered)")
assert(rows[1][1] == "Id", "First row should be header")
end)
-- Test fromCSV with multiple consecutive comments
test("fromCSV multiple consecutive comments", function()
local csv = "# First comment\n# Second comment\n# Third comment\nId,Name\n1,Test"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows (all comments filtered)")
assert(rows[1][1] == "Id", "First row should be header")
end)
-- Test fromCSV with comment in middle of data
test("fromCSV comment in middle", function()
local csv = "Id,Name\n1,Test\n# Middle comment\n2,Test2"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 3, "Should have 3 rows (comment filtered)")
assert(rows[1][1] == "Id", "First row should be header")
assert(rows[2][1] == "1", "Second row should be first data")
assert(rows[3][1] == "2", "Third row should be second data")
end)
-- Test fromCSV with comment at end
test("fromCSV comment at end", function()
local csv = "Id,Name\n1,Test\n# End comment"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows (end comment filtered)")
assert(rows[1][1] == "Id", "First row should be header")
assert(rows[2][1] == "1", "Second row should be data")
end)
-- Test fromCSV with empty comment line
test("fromCSV empty comment", function()
local csv = "#\nId,Name\n1,Test"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows (empty comment filtered)")
assert(rows[1][1] == "Id", "First row should be header")
end)
-- Test fromCSV with comment and headers
test("fromCSV comment with headers enabled", function()
local csv = "#mercenary_profiles\nId,Name,Value\n1,Test,100\n2,Test2,200"
local rows, err = fromCSV(csv, { hasheader = true, hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 data rows")
assert(rows[1].Id == "1", "First row Id should be '1'")
assert(rows[1].Name == "Test", "First row Name should be 'Test'")
assert(rows[2].Id == "2", "Second row Id should be '2'")
end)
-- Test fromCSV with comment and TSV delimiter
test("fromCSV comment with tab delimiter", function()
local csv = "# Comment\nId\tName\n1\tTest"
local rows, err = fromCSV(csv, { delimiter = "\t", hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 rows")
assert(rows[1][1] == "Id", "First row should be header")
assert(rows[2][1] == "1", "Second row first field should be '1'")
end)
-- Test fromCSV with comment and headers and TSV
test("fromCSV comment with headers and TSV", function()
local csv = "#mercenary_profiles\nId\tName\tValue\n1\tTest\t100"
local rows, err = fromCSV(csv, { delimiter = "\t", hasheader = true, hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 1, "Should have 1 data row")
assert(rows[1].Id == "1", "Row Id should be '1'")
assert(rows[1].Name == "Test", "Row Name should be 'Test'")
assert(rows[1].Value == "100", "Row Value should be '100'")
end)
-- Test fromCSV with data field starting with # (not a comment)
test("fromCSV data field starting with hash", function()
local csv = "Id,Name\n1,#NotAComment\n2,Test"
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 3, "Should have 3 rows (data with # not filtered)")
assert(rows[1][1] == "Id", "First row should be header")
assert(rows[2][2] == "#NotAComment", "Second row should have #NotAComment as data")
end)
-- Test fromCSV with quoted field starting with #
test("fromCSV quoted field with hash", function()
local csv = 'Id,Name\n1,"#NotAComment"\n2,Test'
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 3, "Should have 3 rows (quoted # not filtered)")
assert(rows[2][2] == "#NotAComment", "Quoted field with # should be preserved")
end)
-- Test fromCSV with comment after quoted field
test("fromCSV comment after quoted field", function()
local csv = 'Id,Name\n1,"Test"\n# This is a comment\n2,Test2'
local rows, err = fromCSV(csv, { hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 3, "Should have 3 rows (comment filtered)")
assert(rows[2][2] == "Test", "Quoted field should be preserved")
assert(rows[3][1] == "2", "Third row should be second data row")
end)
-- Math function tests
test("min function", function()
assert(min(5, 3) == 3, "min(5, 3) should be 3")
assert(min(-1, 0) == -1, "min(-1, 0) should be -1")
assert(min(10, 10) == 10, "min(10, 10) should be 10")
end)
test("max function", function()
assert(max(5, 3) == 5, "max(5, 3) should be 5")
assert(max(-1, 0) == 0, "max(-1, 0) should be 0")
assert(max(10, 10) == 10, "max(10, 10) should be 10")
end)
test("round function", function()
assert(round(3.14159) == 3, "round(3.14159) should be 3")
assert(round(3.14159, 2) == 3.14, "round(3.14159, 2) should be 3.14")
assert(round(3.5) == 4, "round(3.5) should be 4")
assert(round(3.4) == 3, "round(3.4) should be 3")
assert(round(123.456, 1) == 123.5, "round(123.456, 1) should be 123.5")
end)
test("floor function", function()
assert(floor(3.7) == 3, "floor(3.7) should be 3")
assert(floor(-3.7) == -4, "floor(-3.7) should be -4")
assert(floor(5) == 5, "floor(5) should be 5")
end)
test("ceil function", function()
assert(ceil(3.2) == 4, "ceil(3.2) should be 4")
assert(ceil(-3.2) == -3, "ceil(-3.2) should be -3")
assert(ceil(5) == 5, "ceil(5) should be 5")
end)
-- String function tests
test("upper function", function()
assert(upper("hello") == "HELLO", "upper('hello') should be 'HELLO'")
assert(upper("Hello World") == "HELLO WORLD", "upper('Hello World') should be 'HELLO WORLD'")
assert(upper("123abc") == "123ABC", "upper('123abc') should be '123ABC'")
end)
test("lower function", function()
assert(lower("HELLO") == "hello", "lower('HELLO') should be 'hello'")
assert(lower("Hello World") == "hello world", "lower('Hello World') should be 'hello world'")
assert(lower("123ABC") == "123abc", "lower('123ABC') should be '123abc'")
end)
test("format function", function()
assert(format("Hello %s", "World") == "Hello World", "format should work")
assert(format("Number: %d", 42) == "Number: 42", "format with number should work")
assert(format("%.2f", 3.14159) == "3.14", "format with float should work")
end)
test("trim function", function()
assert(trim(" hello ") == "hello", "trim should remove leading and trailing spaces")
assert(trim(" hello world ") == "hello world", "trim should preserve internal spaces")
assert(trim("hello") == "hello", "trim should not affect strings without spaces")
assert(trim(" ") == "", "trim should handle all spaces")
end)
test("strsplit function", function()
local result = strsplit("a,b,c", ",")
assert(#result == 3, "strsplit should return 3 elements")
assert(result[1] == "a", "First element should be 'a'")
assert(result[2] == "b", "Second element should be 'b'")
assert(result[3] == "c", "Third element should be 'c'")
end)
test("strsplit with default separator", function()
local result = strsplit("a b c")
assert(#result == 3, "strsplit with default should return 3 elements")
assert(result[1] == "a", "First element should be 'a'")
assert(result[2] == "b", "Second element should be 'b'")
assert(result[3] == "c", "Third element should be 'c'")
end)
test("strsplit with custom separator", function()
local result = strsplit("a|b|c", "|")
assert(#result == 3, "strsplit with pipe should return 3 elements")
assert(result[1] == "a", "First element should be 'a'")
assert(result[2] == "b", "Second element should be 'b'")
assert(result[3] == "c", "Third element should be 'c'")
end)
-- Conversion function tests
test("num function", function()
assert(num("123") == 123, "num('123') should be 123")
assert(num("45.67") == 45.67, "num('45.67') should be 45.67")
assert(num("invalid") == 0, "num('invalid') should be 0")
assert(num("") == 0, "num('') should be 0")
end)
test("str function", function()
assert(str(123) == "123", "str(123) should be '123'")
assert(str(45.67) == "45.67", "str(45.67) should be '45.67'")
assert(str(0) == "0", "str(0) should be '0'")
end)
test("is_number function", function()
assert(is_number("123") == true, "is_number('123') should be true")
assert(is_number("45.67") == true, "is_number('45.67') should be true")
assert(is_number("invalid") == false, "is_number('invalid') should be false")
assert(is_number("") == false, "is_number('') should be false")
assert(is_number("123abc") == false, "is_number('123abc') should be false")
end)
-- Table function tests
test("isArray function", function()
assert(isArray({ 1, 2, 3 }) == true, "isArray should return true for sequential array")
assert(isArray({ "a", "b", "c" }) == true, "isArray should return true for string array")
assert(isArray({}) == true, "isArray should return true for empty array")
assert(isArray({ a = 1, b = 2 }) == false, "isArray should return false for map")
assert(isArray({ 1, 2, [4] = 4 }) == false, "isArray should return false for sparse array")
assert(
isArray({ [1] = 1, [2] = 2, [3] = 3 }) == true,
"isArray should return true for 1-indexed array"
)
assert(
isArray({ [0] = 1, [1] = 2 }) == false,
"isArray should return false for 0-indexed array"
)
assert(
isArray({ [1] = 1, [2] = 2, [4] = 4 }) == false,
"isArray should return false for non-sequential array"
)
assert(isArray("not a table") == false, "isArray should return false for non-table")
assert(isArray(123) == false, "isArray should return false for number")
end)
test("fromCSV assigns header keys correctly", function()
local teststr = [[
#mercenary_profiles
Id ModifyStartCost ModifyStep ModifyLevelLimit Health ResistSheet WoundSlots MeleeDamage MeleeAccuracy RangeAccuracy ReceiveAmputationChance ReceiveWoundChanceMult AttackWoundChanceMult Dodge Los StarvationLimit PainThresholdLimit PainThresholdRegen TalentPerkId ActorId SkinIndex HairType HairColorHex VoiceBank Immunity CreatureClass
john_hawkwood_boss 20 0.1 140 blunt 0 pierce 0 lacer 0 fire 0 cold 0 poison 0 shock 0 beam 0 HumanHead HumanShoulder HumanArm HumanThigh HumanFeet HumanChest HumanBody HumanStomach HumanKnee blunt 8 16 crit 1.60 critchance 0.05 0.5 0.5 0.03 0.5 1.2 0.3 8 2200 16 2 talent_the_man_who_sold_the_world human_male 0 hair1 #633D08 player Human
francis_reid_daly 20 0.1 130 blunt 0 pierce 0 lacer 0 fire 0 cold 0 poison 0 shock 0 beam 0 HumanHead HumanShoulder HumanArm HumanThigh HumanFeet HumanChest HumanBody HumanStomach HumanKnee blunt 7 14 crit 1.70 critchance 0.05 0.5 0.4 0.04 0.9 1 0.3 8 2000 10 1 talent_weapon_durability human_male 0 player Human
]]
local rows, err = fromCSV(teststr, { delimiter = "\t", hasheader = true, hascomments = true })
if err then error("fromCSV error: " .. err) end
assert(#rows == 2, "Should have 2 data rows")
-- Test first row
assert(rows[1].Id == "john_hawkwood_boss", "First row Id should be 'john_hawkwood_boss'")
assert(rows[1].ModifyStartCost == "20", "First row ModifyStartCost should be '20'")
assert(rows[1].ModifyStep == "0.1", "First row ModifyStep should be '0.1'")
assert(rows[1].Health == "140", "First row Health should be '140'")
assert(rows[1].ActorId == "human_male", "First row ActorId should be 'human_male'")
assert(rows[1].HairColorHex == "#633D08", "First row HairColorHex should be '#633D08'")
-- Test second row
assert(rows[2].Id == "francis_reid_daly", "Second row Id should be 'francis_reid_daly'")
assert(rows[2].ModifyStartCost == "20", "Second row ModifyStartCost should be '20'")
assert(rows[2].ModifyStep == "0.1", "Second row ModifyStep should be '0.1'")
assert(rows[2].Health == "130", "Second row Health should be '130'")
assert(rows[2].ActorId == "human_male", "Second row ActorId should be 'human_male'")
-- Test that numeric indices still work
assert(rows[1][1] == "john_hawkwood_boss", "First row first field by index should work")
assert(rows[1][2] == "20", "First row second field by index should work")
-- Debug: Print first row keys to verify headers are assigned
print("DEBUG: First row keys:")
for k, v in pairs(rows[1]) do
if type(k) == "string" then print(" " .. k .. " = " .. tostring(v)) end
end
end)
test("fromCSV debug header assignment", function()
local csv = "Id Name Value\n1 Test 100\n2 Test2 200"
local rows, err = fromCSV(csv, { delimiter = "\t", hasheader = true })
if err then error("fromCSV error: " .. err) end
print("DEBUG: Number of rows: " .. #rows)
if #rows > 0 then
print("DEBUG: First row Id = " .. tostring(rows[1].Id))
print("DEBUG: First row Name = " .. tostring(rows[1].Name))
print("DEBUG: First row Value = " .. tostring(rows[1].Value))
print("DEBUG: First row keys:")
for k, v in pairs(rows[1]) do
print(" " .. tostring(k) .. " (" .. type(k) .. ") = " .. tostring(v))
end
end
assert(rows[1].Id == "1", "Id should be '1'")
assert(rows[1].Name == "Test", "Name should be 'Test'")
assert(rows[1].Value == "100", "Value should be '100'")
end)
test("fromCSV real world mercenary file format", function()
local csv = [[#mercenary_profiles
Id ModifyStartCost ModifyStep ModifyLevelLimit Health ResistSheet WoundSlots MeleeDamage MeleeAccuracy RangeAccuracy ReceiveAmputationChance ReceiveWoundChanceMult AttackWoundChanceMult Dodge Los StarvationLimit PainThresholdLimit PainThresholdRegen TalentPerkId ActorId SkinIndex HairType HairColorHex VoiceBank Immunity CreatureClass
john_hawkwood_boss 20 0.1 140 blunt 0 pierce 0 lacer 0 fire 0 cold 0 poison 0 shock 0 beam 0 HumanHead HumanShoulder HumanArm HumanThigh HumanFeet HumanChest HumanBody HumanStomach HumanKnee blunt 8 16 crit 1.60 critchance 0.05 0.5 0.5 0.03 0.5 1.2 0.3 8 2200 16 2 talent_the_man_who_sold_the_world human_male 0 hair1 #633D08 player Human
francis_reid_daly 20 0.1 130 blunt 0 pierce 0 lacer 0 fire 0 cold 0 poison 0 shock 0 beam 0 HumanHead HumanShoulder HumanArm HumanThigh HumanFeet HumanChest HumanBody HumanStomach HumanKnee blunt 7 14 crit 1.70 critchance 0.05 0.5 0.4 0.04 0.9 1 0.3 8 2000 10 1 talent_weapon_durability human_male 0 player Human
]]
local rows, err = fromCSV(csv, { delimiter = "\t", hasheader = true, hascomments = true })
if err then error("fromCSV error: " .. err) end
print("DEBUG: Number of rows: " .. #rows)
assert(#rows == 2, "Should have 2 data rows")
if #rows > 0 then
print("DEBUG: First row Id = " .. tostring(rows[1].Id))
print("DEBUG: First row ModifyStartCost = " .. tostring(rows[1].ModifyStartCost))
print("DEBUG: First row all string keys:")
for k, v in pairs(rows[1]) do
if type(k) == "string" then print(" " .. tostring(k) .. " = " .. tostring(v)) end
end
end
assert(rows[1].Id == "john_hawkwood_boss", "First row Id should be 'john_hawkwood_boss'")
assert(rows[1].ModifyStartCost == "20", "First row ModifyStartCost should be '20'")
assert(rows[2].Id == "francis_reid_daly", "Second row Id should be 'francis_reid_daly'")
end)
print("\nAll tests completed!")