v3.22.2: the runner's TERMINAL RESPONSE inherits the terminal's own objects
Two reported gaps: - a scripted PROVIDE LOCAL INFORMATION expectation answered with an empty TR - the PLI dictionary (and the host clock for the date/time qualifier) was silently inert during runs while the poll path used it all along, so an applet waiting for its data stalled behind a passing expectation. A POLL INTERVAL expectation likewise never echoed the Duration (6.8.4). testscript.build_tr() gained `answer_tlvs` (inserted after the Result per the 6.8.0 order, before text/item/raw) and the shared _test_terminal_response() wires the expect step, the proactive-drain action and the run cleanup to it; the script's `raw` overrides, and a non-success result gets no data object. - a GP INSTALL/LOAD/DELETE step always failed the default exact-9000 check although the envelope SW 91XX is the successful answer (the re-announced SET UP MENU; XX is the pending command's length, never pinnable). Documented rather than loosening the default: a new "Driving a RAM install from a script" subsection in docs/test-scripts.md (the 9??? mask + por any, the per-step pending rule - a proactive-drain/expect after every 91XX step), a sentence at the sw-default bullet, and an scp80 default-pinning test. Docs: api.md's respond clause, help EN/RU's TR Config note. Tests: 839 frontend + 812 Python; sw cache simple-v406.
This commit is contained in:
@@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.22.1'
|
||||
VERSION = '3.22.2'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -6322,6 +6322,26 @@ def _drain_require_label(req):
|
||||
return name + ((' q=%s' % q['value']) if q else '')
|
||||
|
||||
|
||||
def _test_terminal_response(cmd_num, cmd_type, dev_dst, dev_src, cmd_qual, respond):
|
||||
"""The runner's TERMINAL RESPONSE: the scripted answer plus the terminal's
|
||||
own command-specific object(s) - the same objects the terminal's answers
|
||||
carry (`_build_tr`), unless the script supplied `raw` TLVs, which win.
|
||||
A successful PROVIDE LOCAL INFORMATION answer takes the TR Config PLI
|
||||
dictionary entry for its qualifier (or the host clock for the date/time
|
||||
qualifier, TS 102 223 6.4.15); a POLL INTERVAL answer echoes the current
|
||||
interval (6.8.4). Without this a scripted expectation silently stalled an
|
||||
applet that waited for its data (v3.22.2 review)."""
|
||||
respond = respond or {}
|
||||
answer = None
|
||||
if not respond.get('raw') and respond.get('result', 0) == 0x00:
|
||||
if cmd_type == 0x26 and cmd_qual is not None:
|
||||
answer = _pli_data_hex(cmd_qual)
|
||||
elif cmd_type == 0x03:
|
||||
answer = '840201%02X' % _POLL_INTERVAL
|
||||
return testscript.build_tr(cmd_num, cmd_type, dev_dst, dev_src, respond,
|
||||
answer_tlvs=answer or None)
|
||||
|
||||
|
||||
def _test_run_drain(server, step):
|
||||
"""The proactive drain action: poll STATUS and consume every announced
|
||||
command - the TERMINAL RESPONSE is `first` for the first one, `respond`
|
||||
@@ -6358,7 +6378,8 @@ def _test_run_drain(server, step):
|
||||
if menu:
|
||||
server.sim_menu = menu
|
||||
respond = p['first'] if (not consumed and p.get('first')) else p['respond']
|
||||
tr = testscript.build_tr(cmd_num, cmd_type, dev_dst, dev_src, respond)
|
||||
tr = _test_terminal_response(cmd_num, cmd_type, dev_dst, dev_src,
|
||||
cmd_qual, respond)
|
||||
tr_rv = scc._tp.send_apdu('%s140000%02x%s' % (scc.cat_cla, len(tr), tr.hex()))
|
||||
_test_log('TR=%s -> SW=%s' % (tr.hex().upper(), tr_rv[1]))
|
||||
consumed.append({'type_hex': '%02X' % cmd_type, 'type_name': name,
|
||||
@@ -6909,7 +6930,8 @@ def _test_run_expect(server, step, pending, ctx=None):
|
||||
checks.append(_test_check_result(
|
||||
'Files', sorted(paths) == sorted(want), ', '.join(want),
|
||||
', '.join(paths) or '(none)', c['on_fail']))
|
||||
tr = testscript.build_tr(cmd_num, cmd_type, dev_dst, dev_src, step['respond'])
|
||||
tr = _test_terminal_response(cmd_num, cmd_type, dev_dst, dev_src, cmd_qual,
|
||||
step['respond'])
|
||||
tr_rv = scc._tp.send_apdu('%s140000%02x%s' % (scc.cat_cla, len(tr), tr.hex()))
|
||||
tr_sw = tr_rv[1]
|
||||
_test_log('TR=%s -> SW=%s' % (tr.hex().upper(), tr_sw))
|
||||
@@ -6948,7 +6970,8 @@ def _test_drain_pending(scc, fetch_len, result=0x00, max_commands=3):
|
||||
break
|
||||
raw = bytes.fromhex(fdata)
|
||||
cmd_num, cmd_type, dev_src, dev_dst, cmd_qual = _parse_proactive_header(raw)
|
||||
tr = testscript.build_tr(cmd_num, cmd_type, dev_dst, dev_src, {'result': result})
|
||||
tr = _test_terminal_response(cmd_num, cmd_type, dev_dst, dev_src,
|
||||
cmd_qual, {'result': result})
|
||||
rv = scc._tp.send_apdu('%s140000%02x%s' % (scc.cat_cla, len(tr), tr.hex()))
|
||||
name = PROACTIVE_TYPE_NAMES.get(cmd_type, '?')
|
||||
answered.append({'name': name, 'type': cmd_type,
|
||||
|
||||
@@ -606,16 +606,20 @@ def _encode_text(text, dcs):
|
||||
return text.encode('gsm03.38')
|
||||
|
||||
|
||||
def build_tr(cmd_num, cmd_type, dev_dst, dev_src, respond):
|
||||
def build_tr(cmd_num, cmd_type, dev_dst, dev_src, respond, answer_tlvs=None):
|
||||
"""Flat COMPREHENSION-TLV TERMINAL RESPONSE payload (TS 102 223 6.8) in the
|
||||
6.8.0 object order: command details, device identities, Result, then the
|
||||
command-specific objects (Text string for GET INKEY/GET INPUT, Item
|
||||
identifier for SELECT ITEM) and any extra raw TLVs. Matches the
|
||||
interactive menu TR layout."""
|
||||
command-specific objects (the terminal's own `answer_tlvs` - the PLI data
|
||||
of PROVIDE LOCAL INFORMATION, the echoed Duration of POLL INTERVAL - the
|
||||
Text string for GET INKEY/GET INPUT, the Item identifier for SELECT ITEM)
|
||||
and any extra raw TLVs. Matches the interactive menu TR layout; the
|
||||
runner passes the terminal objects the script did not override."""
|
||||
out = bytearray([0x81, 0x03, cmd_num & 0xFF, cmd_type & 0xFF, 0x00])
|
||||
out += bytes([0x82, 0x02, dev_dst & 0xFF, dev_src & 0xFF])
|
||||
result = int(respond.get('result', 0))
|
||||
out += bytes([0x83, 0x02, result & 0xFF, 0x00])
|
||||
if answer_tlvs:
|
||||
out += bytes.fromhex(answer_tlvs)
|
||||
if respond.get('text') is not None:
|
||||
text = str(respond['text'])
|
||||
if text == '':
|
||||
|
||||
Reference in New Issue
Block a user