0
0
Fork 0
Codesys-MCP-SP21-plus/tests/unit/var-block-parse.test.ts
Karstein Phobic Nyvold Kvistad c81ce33a4b feat(live-values): pump skeleton + VAR-block parser
Task 6 of v0.3 plan (parser + pump class; full --live-values CLI
wiring is Task 7).

parseVarNames(text) extracts variable names from any VAR /
VAR_INPUT / VAR_OUTPUT / VAR_GLOBAL / etc. block. Handles:
  - one name per declaration line
  - 'AT %QX0.1' location prefix
  - := initializer
  - inline (* ... *) and // line comments
  - multi-line (* ... *) blocks (state threaded across lines)
Doesn't handle the multi-name shorthand 'a, b : INT;' -- vanishingly
rare in practice, cost of getting it wrong is just a missing overlay.

LiveValuesPump owns a setInterval. Each tick:
  - reads tui-state.json via the injected readSelection
  - reads the POU's .st file from the mirror
  - parseVarNames -> per-var read_variable round-trip (errors per-var
    are silent, others may succeed)
  - writeLiveValues snapshot

Reentrancy guard: if a tick is still in flight when the next
interval fires, skip the new tick rather than queueing. Errors at any
layer are swallowed -- pump must never crash the server.

start()/stop() lifecycle so the server can hand it to the existing
shutdown path.
2026-04-29 09:55:18 +02:00

57 lines
1.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { parseVarNames } from '../../src/live-values-pump';
describe('parseVarNames', () => {
it('extracts vars from a single VAR block', () => {
const text = [
'PROGRAM PLC_PRG',
'VAR',
' counter : INT := 0;',
' bRunning : BOOL;',
' rTemperature : REAL;',
'END_VAR',
].join('\n');
expect(parseVarNames(text)).toEqual(['counter', 'bRunning', 'rTemperature']);
});
it('handles VAR_INPUT / VAR_OUTPUT / VAR_GLOBAL too', () => {
const text = [
'FUNCTION_BLOCK FB_X',
'VAR_INPUT',
' x : INT;',
'END_VAR',
'VAR_OUTPUT',
' y : BOOL;',
'END_VAR',
'VAR',
' internal : DINT;',
'END_VAR',
].join('\n');
expect(parseVarNames(text).sort()).toEqual(['internal', 'x', 'y']);
});
it('ignores lines inside (* ... *) blocks and after //', () => {
const text = [
'VAR',
' alpha : INT;',
' (* commentedOut : BOOL; *)',
' beta : INT; // tail comment',
'END_VAR',
].join('\n');
expect(parseVarNames(text)).toEqual(['alpha', 'beta']);
});
it('returns [] when no VAR block is present', () => {
expect(parseVarNames('PROGRAM X\nEND_PROGRAM')).toEqual([]);
});
it('handles AT %X10.0 location prefix and := initializer', () => {
const text = [
'VAR',
' bRelay AT %QX0.1 : BOOL := FALSE;',
' iCount : INT := 42;',
'END_VAR',
].join('\n');
expect(parseVarNames(text)).toEqual(['bRelay', 'iCount']);
});
});