local test = dofile("test/test_harness.lua") package.path = package.path .. ";lua/?.lua" local udt_parser = require("scl.udt_parser") local function test_udt_parser_basic() local content = [[ TYPE "test_udt" TITLE = Test UDT VERSION : 0.1 //Test type definition STRUCT member1 : Bool; member2 : Int; member3 : "other_udt"; END_STRUCT; END_TYPE ]] local udt, err = udt_parser.parse_udt_content(content, "test.udt") if udt then udt_parser.register_inline_udt(udt) end test.assert_not_nil(udt, "parse_udt_content should return udt") test.assert_equals(udt.name, "test_udt", "type name should be extracted") test.assert_equals(#udt.members, 3, "should have 3 members") test.assert_equals(udt.members[1].name, "member1", "first member name") test.assert_equals(udt.members[1].type, "Bool", "first member type") test.assert_equals(udt.members[3].is_udt, true, "third member is UDT type") end local function test_udt_parser_attributes() local content = [[ TYPE "attr_test" TITLE = Attr Test VERSION : 0.1 STRUCT attrMember{S7_SetPoint := 'True'}: Bool; normalMember : Int; END_STRUCT; END_TYPE ]] local udt = udt_parser.parse_udt_content(content, "attr.udt") test.assert_not_nil(udt, "parse_udt_content should return udt") test.assert_equals(udt.members[1].attributes.S7_SetPoint, "True", "attribute should be parsed") test.assert(udt.members[2].attributes == nil or next(udt.members[2].attributes) == nil, "normal member has no attributes") end local function test_udt_parser_comments() local content = [[ TYPE "comment_test" TITLE = Comment Test VERSION : 0.1 //This is a type comment STRUCT member1 : Bool; //first member member2 : Int; //second member END_STRUCT; END_TYPE ]] local udt = udt_parser.parse_udt_content(content, "comment.udt") test.assert_not_nil(udt, "parse_udt_content should return udt") test.assert_equals(udt.comment, "This is a type comment", "type comment should be extracted") test.assert_equals(udt.members[1].comment, "first member", "member comment should be extracted") end local function test_udt_parser_array() local content = [[ TYPE "array_test" TITLE = Array Test VERSION : 0.1 STRUCT arr : Array[0..9] of Int; arr2 : Array[0..5, 0..3] of Bool; END_STRUCT; END_TYPE ]] local udt = udt_parser.parse_udt_content(content, "array.udt") test.assert_not_nil(udt, "parse_udt_content should return udt") test.assert(udt.members[1].type:match("Array"), "array type should be preserved") end local function test_udt_cache_operations() udt_parser.clear_cache() test.assert_equals(udt_parser.get_cache_count(), 0, "cache should be empty after clear") local content = [[ TYPE "cached_udt" TITLE = Cached UDT VERSION : 0.1 STRUCT m1 : Bool; END_STRUCT; END_TYPE ]] local udt = udt_parser.parse_udt_content(content, "cached.udt") if udt then udt_parser.register_inline_udt(udt) end local cached = udt_parser.get_udt("cached_udt") test.assert_not_nil(cached, "UDT should be cached") test.assert_equals(udt_parser.get_cache_count(), 1, "cache should have 1 entry") local names = udt_parser.get_all_udt_names() test.assert_equals(#names, 1, "should have 1 UDT name") test.assert_equals(names[1], "cached_udt", "UDT name should match") end local function test_udt_is_type() udt_parser.clear_cache() local content = [[ TYPE "known_type" TITLE = Known VERSION : 0.1 STRUCT m1 : Bool; END_STRUCT; END_TYPE ]] local udt = udt_parser.parse_udt_content(content, "known.udt") if udt then udt_parser.register_inline_udt(udt) end test.assert(udt_parser.is_udt_type("known_type"), "known_type should be recognized") test.assert(udt_parser.is_udt_type('"known_type"'), "quoted known_type should be recognized") test.assert(not udt_parser.is_udt_type("unknown_type"), "unknown_type should not be recognized") end local function test_udt_members() udt_parser.clear_cache() local content1 = [[ TYPE "inner_type" TITLE = Inner VERSION : 0.1 STRUCT x : Int; END_STRUCT; END_TYPE ]] local content2 = [[ TYPE "outer_type" TITLE = Outer VERSION : 0.1 STRUCT inner : "inner_type"; simple : Bool; END_STRUCT; END_TYPE ]] local udt1 = udt_parser.parse_udt_content(content1, "inner.udt") local udt2 = udt_parser.parse_udt_content(content2, "outer.udt") if udt1 then udt_parser.register_inline_udt(udt1) end if udt2 then udt_parser.register_inline_udt(udt2) end local members = udt_parser.get_udt_members("outer_type") test.assert_not_nil(members, "members should not be nil") if members and #members > 0 then test.assert_equals(members[1].name, "inner", "first member name") test.assert_equals(members[1].is_udt, true, "first member is UDT type") end end local function test_udt_reference_files() udt_parser.clear_cache() local ref_path = test.REF_PROJECT .. "/PLC data types/99_Library/Manz Siemens Standard/01_Machine/LMS_typePartCounter.udt" local file = io.open(ref_path, "r") if not file then print("Warning: Reference UDT file not found: " .. ref_path) return end local content = file:read("*all") file:close() local udt = udt_parser.parse_udt_content(content, ref_path) if udt then udt_parser.register_inline_udt(udt) end test.assert_not_nil(udt, "should parse reference UDT file") test.assert_equals(udt.name, "LMS_typePartCounter", "UDT name should match") test.assert_gt(#udt.members, 0, "should have members") local cached = udt_parser.get_udt("LMS_typePartCounter") test.assert_not_nil(cached, "UDT should be cached") end local function test_udt_multiple_reference_files() udt_parser.clear_cache() local base_path = test.REF_PROJECT .. "/PLC data types/99_Library/Manz Siemens Standard/" local files = { base_path .. "01_Machine/LMS_typePartCounter.udt", base_path .. "01_Machine/LMS_typePartCounterCommands.udt", base_path .. "01_Machine/LMS_typePartCounterStatus.udt", } local parsed_count = 0 for _, filepath in ipairs(files) do local file = io.open(filepath, "r") if file then local content = file:read("*all") file:close() local udt = udt_parser.parse_udt_content(content, filepath) if udt then udt_parser.register_inline_udt(udt) parsed_count = parsed_count + 1 end end end test.assert_gt(parsed_count, 0, "should parse at least one reference UDT") test.assert_equals(udt_parser.get_cache_count(), parsed_count, "cache should have correct count") end local function run_udt_tests() test.run_suite("UDT Parser Tests", function() print("\n--- Basic Tests ---") test_udt_parser_basic() test_udt_parser_attributes() test_udt_parser_comments() test_udt_parser_array() print("\n--- Cache Tests ---") test_udt_cache_operations() test_udt_is_type() test_udt_members() print("\n--- Reference Project Tests ---") test_udt_reference_files() test_udt_multiple_reference_files() end) end if vim then vim.api.nvim_create_user_command("TestUdt", function() run_udt_tests() end, {}) end run_udt_tests()