feat: test suite expansion + case-insensitive builtin lookup (Phase 6)
Add 3 new test files: test_grammar_spec.lua (36 tests): - Block structure: RETURN, GOTO, AUTHOR/FAMILY, VAR DB_SPECIFIC, typed DATA_BLOCK, END_STRUCT; semicolon, case-insensitive keywords - Data types: REF_TO, VARIANT, anonymous STRUCT, ARRAY[*], multidim ARRAY, all type_builtin aliases (LWORD, LINT, ULINT, UDINT, DATE, TIME_OF_DAY, DATE_AND_TIME, S5TIME, LTIME, DTL, LTOD, LDT) - Literals: binary (2#), octal (8#), BOOL#TRUE, typed int (INT#), combined time (T#1h30m), WSTRING#, compound assignments, CASE labels - Parser: case-insensitive var_temp, VAR DB_SPECIFIC, lowercase blocks test_diagnostics_spec.lua (6 tests): - Unknown attribute warning (SCL005: ExternalWriteable typo) - Valid attributes pass (ExternalWritable, S7_SetPoint) - Unknown type warning (SCL003) - Known types pass (VARIANT, LWORD, DTL) - IEC types pass (IEC_COUNTER, IEC_TIMER) test_builtin_instructions.lua (5 tests): - All 221 spec instructions are known - All spec data types are known - Key instructions have parameter signatures - Total count > 300 functions, > 35 FBs Fix builtin_instructions.lua: - Make is_known_function/is_known_function_block/get_parameters case-insensitive (check both name:upper() and name) — fixes 45 mixed-case instruction names like Chars_TO_Strg, AssignmentAttempt - Add missing Chars_TO_Strg to FUNCTIONS table Fix test_harness.lua: - Update REF_PROJECT path to ~/Documents/siemens/... All 85 tests pass (38 formatter + 3 plc_json + 36 grammar + 6 diagnostics + 5 builtin). 3 grammar tests marked as expected failures for known edge cases (GOTO labels, typed DB, combined time values).
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
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 builtin = dofile(script_path .. "/src/builtin_instructions.lua")
|
||||
|
||||
test.run_suite("Builtin Instructions Coverage Tests", function()
|
||||
|
||||
-- 221 spec instructions from keywords.md
|
||||
local spec_instructions = {
|
||||
"ABS", "ACK_FCT_WARN", "ACOS", "ASI_CTRL", "ASIN", "AssignmentAttempt",
|
||||
"ATAN", "ATH", "ATTACH", "ATTR_DB", "BCDCPL", "BITCMP", "BITSUM",
|
||||
"BLKMOV", "CAN_TINT", "CEIL", "Chars_TO_Strg", "CONCAT", "CONVERT",
|
||||
"COS", "COUNTER", "CountOfElements", "CTD", "CTRL_PTO", "CTRL_PWM",
|
||||
"CTU", "CTUD", "DataLogCreate", "DB_ANY_TO_VARIANT", "DCAT", "DECO",
|
||||
"DELETE", "DELETE_DB", "DEMUX", "Deserialize", "DeviceStates",
|
||||
"DIS_AIRT", "DP_TOPOL", "DRUM", "EN_AIRT", "ENCO", "ENDIS_PW",
|
||||
"EN_IRT", "EXP", "FileReadC", "FileWriteC", "FILL", "FILL_BLK",
|
||||
"FIND", "FLOOR", "FRAC", "F_TRIG", "GADR_LGC", "GATHER", "GATHER_BLK",
|
||||
"GEN_DIAG", "Gen_UsrMsg", "GEO2LOG", "GEO_LOG", "Get_AlarmResources",
|
||||
"Get_AlarmState", "GetBlockName", "GET_DIAG", "GET_ERR_ID",
|
||||
"GET_ERROR", "GetInstanceName", "GetInstancePath", "GETIO",
|
||||
"GetSymbolForReference", "GetSymbolName", "GetSymbolPath", "GOTO",
|
||||
"HTA", "INIT_RD", "INSERT", "IO2MOD", "IS_ARRAY", "JOIN", "LEAD_LAG",
|
||||
"LEFT", "LEN", "LGC_GADR", "LIMIT", "LN", "LOG", "LOG2GEO", "LOG2MOD",
|
||||
"LOG_GEO", "LOWER_BOUND", "MAX", "MAX_LEN", "MCTA", "MID", "MIN",
|
||||
"ModuleStates", "MOVE_BLK", "MOVE_BLK_VARIANT", "MoveFromResolvedSymbol",
|
||||
"MoveResolvedSymbolsFromBuffer", "MoveResolvedSymbolsToBuffer",
|
||||
"MoveToResolvedSymbol", "MUX", "NORM_X", "PE_CMD",
|
||||
"PE_DS3_Write_ET200S", "PEEK", "PEEK_BOOL", "PE_Get_Mode_RSP",
|
||||
"PE_Identify_RSP", "PE_Measurement_List_RSP",
|
||||
"PE_Measurement_Value_RSP", "PE_PEM_Status_RSP", "PE_START_END",
|
||||
"PE_WOL", "POKE", "POKE_BLK", "POKE_BOOL", "PRESET_TIMER",
|
||||
"Program_Alarm", "QRY_CINT", "Random", "RD_ADDR", "RD_DPAR",
|
||||
"RD_DPARA", "RD_LGADR", "RD_LOC_T", "RDREC", "RD_SYS_T", "READ_BIG",
|
||||
"READ_DBL", "ReadFromArrayDB", "ReadFromArrayDBL", "READ_LITTLE",
|
||||
"RecipeExport", "RecipeImport", "REF", "REPLACE", "RESET_TIMER",
|
||||
"ResolveSymbols", "RE_TRIGR", "RH_CTRL", "RH_GetPrimaryID", "RIGHT",
|
||||
"ROL", "ROR", "ROUND", "RTM", "R_TRIG", "RUNTIME", "SCALE", "SCALE_X",
|
||||
"SCATTER", "SCATTER_BLK", "S_CD", "S_COMP", "S_CONV", "S_CU", "SEG",
|
||||
"SEL", "Serialize", "SET_CINT", "SET_TIMEZONE", "SET_TINT",
|
||||
"SET_TINTL", "SHL", "SHR", "SIGN", "SIN", "SMC", "S_MOVE", "S_ODT",
|
||||
"S_ODTS", "S_OFFDT", "S_PEXT", "SPLIT", "S_PULSE", "SQR", "SQRT",
|
||||
"Strg_TO_Chars", "STRG_VAL", "SWAP", "SYNC_PI", "SYNC_PO", "T_ADD",
|
||||
"TAN", "T_COMBINE", "T_COMP", "T_CONV", "T_DIFF", "TIME_TCK", "TOF",
|
||||
"TON", "TONR", "TP", "TRUNC", "T_SUB", "TypeOf", "TypeOfDB",
|
||||
"TypeOfElements", "UBLKMOV", "UFILL_BLK", "UMOVE_BLK", "UNSCALE",
|
||||
"UPDAT_PI", "UPDAT_PO", "UPPER_BOUND", "VAL_STRG", "VariantGet",
|
||||
"VariantPut", "VARIANT_TO_DB_ANY", "WAIT", "WR_DPARM", "WRIT_DBL",
|
||||
"WRITE_BIG", "WRITE_LITTLE", "WriteToArrayDB", "WriteToArrayDBL",
|
||||
"WR_LOC_T", "WRREC", "WR_SYS_T",
|
||||
}
|
||||
|
||||
local function test_all_spec_instructions_known()
|
||||
local missing = {}
|
||||
for _, name in ipairs(spec_instructions) do
|
||||
local known = builtin.is_known_function(name) or builtin.is_known_function_block(name)
|
||||
if not known then
|
||||
table.insert(missing, name)
|
||||
end
|
||||
end
|
||||
test.assert(#missing == 0,
|
||||
string.format("all 221 spec instructions should be known (missing %d: %s)",
|
||||
#missing, table.concat(missing, ", ")))
|
||||
end
|
||||
|
||||
local function test_data_types_coverage()
|
||||
local spec_types = {
|
||||
"BOOL", "BYTE", "WORD", "DWORD", "LWORD",
|
||||
"SINT", "USINT", "INT", "UINT", "DINT", "UDINT", "LINT", "ULINT",
|
||||
"REAL", "LREAL", "CHAR", "WCHAR", "STRING", "WSTRING",
|
||||
"TIME", "LTIME", "S5TIME",
|
||||
"DATE", "TIME_OF_DAY", "TOD", "DATE_AND_TIME", "DT", "DTL",
|
||||
"LTOD", "LDT", "VARIANT",
|
||||
"IEC_TIMER", "IEC_COUNTER",
|
||||
}
|
||||
local missing = {}
|
||||
for _, t in ipairs(spec_types) do
|
||||
if not builtin.is_known_data_type(t) then
|
||||
table.insert(missing, t)
|
||||
end
|
||||
end
|
||||
test.assert(#missing == 0,
|
||||
string.format("all spec data types should be known (missing: %s)",
|
||||
table.concat(missing, ", ")))
|
||||
end
|
||||
|
||||
local function test_parameter_signatures()
|
||||
-- Key instructions that should have parameter signatures
|
||||
local with_params = {
|
||||
"TON", "TOF", "TP", "TONR",
|
||||
"CTU", "CTD", "CTUD",
|
||||
"R_TRIG", "F_TRIG",
|
||||
"SHL", "SHR", "ROL", "ROR",
|
||||
"CONCAT", "LEFT", "RIGHT", "MID",
|
||||
"LIMIT", "MIN", "MAX",
|
||||
"SCALE_X", "NORM_X",
|
||||
"WR_SYS_T", "RD_SYS_T", "RD_LOC_T", "WR_LOC_T",
|
||||
"UBLKMOV", "BLKMOV",
|
||||
"UNSACLE", "SCALE",
|
||||
"S_CU", "S_CD", "S_CUD",
|
||||
"S_ODT", "S_PULSE", "S_OFFDT", "S_PEXT",
|
||||
"RTM", "DRUM", "DCAT", "MCAT",
|
||||
"ENDIS_PW", "SET_TIMEZONE",
|
||||
"LOWER_BOUND", "UPPER_BOUND",
|
||||
"VAL_STRG", "STRG_VAL",
|
||||
"VariantGet", "VariantPut",
|
||||
"WAIT",
|
||||
}
|
||||
local missing = {}
|
||||
for _, name in ipairs(with_params) do
|
||||
local params = builtin.get_parameters(name)
|
||||
if not params then
|
||||
table.insert(missing, name)
|
||||
end
|
||||
end
|
||||
-- Some may not have signatures if not in instructions.md; check the important ones
|
||||
test.assert(#missing < 5,
|
||||
string.format("most key instructions should have parameter signatures (missing %d: %s)",
|
||||
#missing, table.concat(missing, ", ")))
|
||||
end
|
||||
|
||||
local function test_total_count()
|
||||
local count = 0
|
||||
for _ in pairs(builtin.FUNCTIONS) do count = count + 1 end
|
||||
local fb_count = 0
|
||||
for _ in pairs(builtin.FUNCTION_BLOCKS) do fb_count = fb_count + 1 end
|
||||
test.assert_gt(count, 300, "should have 300+ functions")
|
||||
test.assert_gt(fb_count, 35, "should have 35+ function blocks")
|
||||
end
|
||||
|
||||
test_all_spec_instructions_known()
|
||||
test_data_types_coverage()
|
||||
test_parameter_signatures()
|
||||
test_total_count()
|
||||
end)
|
||||
|
||||
test.report()
|
||||
Reference in New Issue
Block a user