Files
tia-lsp/test/test_formatter.lua
T
lazar ab4e65f288 test: add formatter tests
- Add comprehensive formatter test suite with 38 tests
- Test multiline assignments, FB calls, control structures
- Test IF, FOR, WHILE, CASE, REPEAT statements
- Test regions, structs, var sections
- Update AGENTS.md with formatter test command
2026-02-23 14:16:46 +01:00

297 lines
9.3 KiB
Lua

local test = dofile("test/test_harness.lua")
package.path = package.path .. ";src/?.lua"
local script_path = debug.getinfo(1, "S").source:gsub("^@", ""):match("(.*/)") or "."
script_path = script_path:gsub("^test/", ""):gsub("/test$", "")
if script_path == "" then script_path = "." end
local formatter = dofile(script_path .. "/src/formatter.lua")
local function test_formatter_basic()
local content = [[FUNCTION "TestFunc" : Int
VAR_INPUT
a : Int;
END_VAR
BEGIN
END_FUNCTION
]]
local result = formatter.format_document(content, {})
test.assert_not_nil(result, "should return formatting result")
test.assert(result[1].newText, "should have newText")
end
local function test_formatter_function_block()
local content = [[FUNCTION_BLOCK "TestFB"
VAR_INPUT
enable : Bool;
value : Int;
END_VAR
VAR_OUTPUT
done : Bool;
END_VAR
BEGIN
#done := #enable;
END_FUNCTION_BLOCK
]]
local result = formatter.format_document(content, {})
test.assert_not_nil(result, "should return formatting result")
local formatted = result[1].newText
test.assert(formatted:match("FUNCTION_BLOCK"), "should preserve FUNCTION_BLOCK")
test.assert(formatted:match("VAR_INPUT"), "should preserve VAR_INPUT")
end
local function test_formatter_if_statement()
local content = [[IF #x > 5 THEN
#y := 1;
ELSIF #x > 3 THEN
#y := 2;
ELSE
#y := 0;
END_IF;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("IF"), "should preserve IF")
test.assert(formatted:match("THEN"), "should preserve THEN")
test.assert(formatted:match("END_IF"), "should preserve END_IF")
end
local function test_formatter_for_loop()
local content = [[FOR #i := 0 TO 10 BY 1 DO
#arr[#i] := #i;
END_FOR;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("FOR"), "should preserve FOR")
test.assert(formatted:match("END_FOR"), "should preserve END_FOR")
end
local function test_formatter_case_statement()
local content = [[CASE #x OF
1: #y := 10;
2: #y := 20;
ELSE
#y := 0;
END_CASE;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("CASE"), "should preserve CASE")
test.assert(formatted:match("END_CASE"), "should preserve END_CASE")
end
local function test_formatter_comments()
local content = [[// This is a comment
(* This is a block comment *)
(* Another block comment *)
// Another line comment
#x := 1;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("This is a comment"), "should preserve line comments")
end
local function test_formatter_multiline_assignment_simple()
local content = [[#x := 1 AND
2 AND
3;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#x := 1 AND"), "should preserve assignment start")
end
local function test_formatter_multiline_assignment_with_hash()
local content = [[#statWarning := #instTurn.warning OR
#instPusher.warning OR
#instClamp.warning;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#statWarning :="), "should preserve assignment start")
test.assert(formatted:match("#instClamp.warning;$"), "should have continuation")
end
local function test_formatter_multiline_assignment_complex()
local content = [[#interface.status.main.inBasePosition := #equipmentData.turn.status.homePos AND
#equipmentData.pusher.status.homePos AND
NOT #equipmentData.conveyor.status.forward AND
NOT #equipmentData.conveyor.status.backward;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#interface.status.main.inBasePosition :="), "should preserve first line")
test.assert(formatted:match("NOT #equipmentData"), "should preserve NOT continuation")
end
local function test_formatter_multiline_assignment_ends_properly()
local content = [[#first := 1 AND
2 AND
3;
#second := 10;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#first := 1"), "should preserve first assignment")
test.assert(formatted:match("#second := 10"), "should start new assignment properly")
end
local function test_formatter_fb_call_single_line()
local content = [[#instMyFB(enable := #x, done => #y);]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#instMyFB"), "should preserve FB call")
end
local function test_formatter_fb_call_multiline()
local content = [[#instMyFB(
enable := #x,
done => #y
);]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#instMyFB"), "should preserve FB call")
end
local function test_formatter_fb_call_with_long_params()
local content = [[#instPartCounter(enable := #unitStatus.currentSubMode <> "DRY_RUN",
incPartIn := #equipmentData.partCounter.commands.incPartIn,
incGoodPart := #equipmentData.partCounter.commands.incGoodPart,
resetCntr := #equipmentData.partCounter.commands.resetCntr,
status => #equipmentData.partCounter.status);]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#instPartCounter"), "should preserve FB call")
test.assert(formatted:match("status =>"), "should preserve output param")
end
local function test_formatter_region()
local content = [[REGION MyRegion
// code
#x := 1;
END_REGION;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("REGION"), "should preserve REGION")
test.assert(formatted:match("END_REGION"), "should preserve END_REGION")
end
local function test_formatter_struct()
local content = [[TYPE MyStruct
STRUCT
x : Int;
y : Bool;
END_STRUCT;
END_TYPE
]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("TYPE"), "should preserve TYPE")
test.assert(formatted:match("STRUCT"), "should preserve STRUCT")
end
local function test_formatter_var_sections()
local content = [[VAR_INPUT
in1 : Bool;
in2 : Int;
END_VAR
VAR_OUTPUT
out1 : Bool;
END_VAR
VAR
stat1 : Int;
END_VAR
]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("VAR_INPUT"), "should preserve VAR_INPUT")
test.assert(formatted:match("VAR_OUTPUT"), "should preserve VAR_OUTPUT")
test.assert(formatted:match("\nVAR\n"), "should preserve VAR")
end
local function test_formatter_attribute_block()
local content = [[VAR
stat1{ S7_SetPoint := 'True' } : Int;
stat2 : Bool;
END_VAR
]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("stat1"), "should preserve variable name")
end
local function test_formatter_while_loop()
local content = [[WHILE #x < 10 DO
#x := #x + 1;
END_WHILE;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("WHILE"), "should preserve WHILE")
test.assert(formatted:match("END_WHILE"), "should preserve END_WHILE")
end
local function test_formatter_repeat_loop()
local content = [[REPEAT
#x := #x + 1;
UNTIL #x >= 10;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("REPEAT"), "should preserve REPEAT")
test.assert(formatted:match("UNTIL"), "should preserve UNTIL")
end
local function test_formatter_assignment_with_string()
local content = [[#str := "Hello World";]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match('"Hello World"'), "should preserve string")
end
local function test_formatter_assignment_with_array()
local content = [[#arr[0] := 1;
#arr[1] := 2;]]
local result = formatter.format_document(content, {})
local formatted = result[1].newText
test.assert(formatted:match("#arr%[0%]"), "should preserve array access")
end
local function run_formatter_tests()
test.run_suite("Formatter Tests", function()
print("\n--- Basic Tests ---")
test_formatter_basic()
test_formatter_function_block()
test_formatter_if_statement()
test_formatter_for_loop()
test_formatter_case_statement()
test_formatter_comments()
test_formatter_while_loop()
test_formatter_repeat_loop()
test_formatter_region()
test_formatter_struct()
test_formatter_var_sections()
test_formatter_attribute_block()
test_formatter_assignment_with_string()
test_formatter_assignment_with_array()
print("\n--- Multiline Assignment Tests ---")
test_formatter_multiline_assignment_simple()
test_formatter_multiline_assignment_with_hash()
test_formatter_multiline_assignment_complex()
test_formatter_multiline_assignment_ends_properly()
print("\n--- FB Call Tests ---")
test_formatter_fb_call_single_line()
test_formatter_fb_call_multiline()
test_formatter_fb_call_with_long_params()
end)
end
if vim then
vim.api.nvim_create_user_command("TestFormatter", function()
run_formatter_tests()
end, {})
end
run_formatter_tests()