- 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
297 lines
9.3 KiB
Lua
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()
|