v3.22.1: rename the proactive action kind to proactive-drain
The kind string `proactive` read like an expectation of a proactive command
(`expect` is the step type for that) although the action consumes and answers
them. Renamed everywhere:
- ACTION_KINDS / TEST_ACTION_KINDS (drift-tested), the normaliser branch and
its error strings, the dispatch and the helper names (_test_run_drain,
_drain_require_match/_label);
- the report label is DRAIN and the PWA summary reads "DRAIN x5 require ...
TR ok";
- the editor/collect/summary/problem branches, docs/test-scripts.md
(Proactive drain), docs/api.md, help EN/RU ("Очистка очереди proactive");
- the tests (TestRunnerProactiveDrain, the normalisation/data-check tests).
No stored scripts used the old kind (unpublished series), so there is no
alias and no migration. sw cache simple-v404.
Tests: 839 frontend + 804 Python green.
This commit is contained in:
@@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.22.0'
|
||||
VERSION = '3.22.1'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -5853,7 +5853,7 @@ _TEST_KIND_LABELS = {
|
||||
'menu-select': 'ENVELOPE(Menu Selection)',
|
||||
'file-write': 'UPDATE FILE', 'file-read': 'READ FILE', 'apdu': 'APDU',
|
||||
'scp80': 'SCP80', 'status': 'STATUS', 'cleanup': 'CLEANUP',
|
||||
'proactive': 'PROACTIVE',
|
||||
'proactive-drain': 'DRAIN',
|
||||
}
|
||||
# Card-touching endpoints refused while a script owns the card.
|
||||
_TEST_BLOCKED_PATHS = frozenset([
|
||||
@@ -6300,8 +6300,8 @@ def _test_run_status(scc, step):
|
||||
return data or '', sw, ('STATUS x%d' % used if used > 1 else 'STATUS')
|
||||
|
||||
|
||||
def _proactive_require_match(req, consumed):
|
||||
"""Whether one consumed command satisfies a proactive action's `require`
|
||||
def _drain_require_match(req, consumed):
|
||||
"""Whether one consumed command satisfies a proactive-drain action's `require`
|
||||
spec: the command type/name (ANY matches all) and an optional qualifier
|
||||
(exact/mask)."""
|
||||
if req.get('type') is not None and consumed.get('type_hex') != '%02X' % req['type']:
|
||||
@@ -6312,7 +6312,7 @@ def _proactive_require_match(req, consumed):
|
||||
return True
|
||||
|
||||
|
||||
def _proactive_require_label(req):
|
||||
def _drain_require_label(req):
|
||||
"""The `require` spec as the report's expected string."""
|
||||
if req.get('type') is not None:
|
||||
name = req.get('name') or '0x%02X' % req['type']
|
||||
@@ -6322,8 +6322,8 @@ def _proactive_require_label(req):
|
||||
return name + ((' q=%s' % q['value']) if q else '')
|
||||
|
||||
|
||||
def _test_run_proactive(server, step):
|
||||
"""The proactive cleanup action: poll STATUS and consume every announced
|
||||
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`
|
||||
for the rest - until the card answers 9000 (idle), a non-91XX SW appears
|
||||
or the attempt budget is spent. The final SW is the step's SW check; an
|
||||
@@ -6376,7 +6376,7 @@ def _test_run_proactive(server, step):
|
||||
step['on_fail'])]
|
||||
req = p.get('require')
|
||||
if req:
|
||||
matched = any(_proactive_require_match(req, c) for c in consumed)
|
||||
matched = any(_drain_require_match(req, c) for c in consumed)
|
||||
if consumed:
|
||||
actual = ', '.join(
|
||||
'%s%s' % (c['type_name'],
|
||||
@@ -6385,7 +6385,7 @@ def _test_run_proactive(server, step):
|
||||
else:
|
||||
actual = '(nothing was pending)'
|
||||
checks.append(_test_check_result('Require', matched,
|
||||
_proactive_require_label(req), actual,
|
||||
_drain_require_label(req), actual,
|
||||
step['on_fail']))
|
||||
status = testscript.combine_levels([c['level'] if not c['ok'] else 'ok'
|
||||
for c in checks])
|
||||
@@ -6646,8 +6646,8 @@ def _test_run_action(server, step, ctx):
|
||||
(' RESP=%dB' % (len(data) // 2)) if data else ''))
|
||||
elif kind == 'scp80':
|
||||
data, sw, sent, por, counter = _test_run_scp80(server, step, ctx)
|
||||
elif kind == 'proactive':
|
||||
return _test_run_proactive(server, step)
|
||||
elif kind == 'proactive-drain':
|
||||
return _test_run_drain(server, step)
|
||||
else:
|
||||
raise testscript.ScriptError('unknown action kind %r' % kind)
|
||||
checks = _test_action_checks(step, sw, data, por, kind)
|
||||
@@ -6759,7 +6759,7 @@ def _test_run_expect(server, step, pending, ctx=None):
|
||||
if not pending:
|
||||
# Nothing to fetch: the step fails at its own on_fail level. The
|
||||
# tolerant "consume a pending command if there is one" form is an
|
||||
# expectation with on_fail "warning" (or the proactive cleanup action,
|
||||
# expectation with on_fail "warning" (or the proactive-drain action,
|
||||
# which tolerates an empty card by design). The UICC announces
|
||||
# pending commands in the response to a command (TS 102 221 7.4.2.1).
|
||||
want = step['command'].get('name')
|
||||
@@ -6936,7 +6936,7 @@ def _test_drain_pending(scc, fetch_len, result=0x00, max_commands=3):
|
||||
make the application emit another, so the drain keeps fetching while the
|
||||
TERMINAL RESPONSE answers 91XX (bounded by `max_commands`). A script that
|
||||
wants to test the refusal path answers explicitly (an expectation's
|
||||
`respond`, or the proactive action's `first`)."""
|
||||
`respond`, or the `proactive-drain` action's `first`)."""
|
||||
answered = []
|
||||
try:
|
||||
while len(answered) < max_commands:
|
||||
|
||||
@@ -33,7 +33,7 @@ __all__ = [
|
||||
]
|
||||
|
||||
ACTION_KINDS = ('envelope', 'event', 'menu-select', 'file-write', 'file-read',
|
||||
'apdu', 'scp80', 'status', 'proactive')
|
||||
'apdu', 'scp80', 'status', 'proactive-drain')
|
||||
FAIL_LEVELS = ('error', 'warning')
|
||||
POR_CHECKS = ('none', 'ok', 'any')
|
||||
|
||||
@@ -357,28 +357,28 @@ def _normalise_params(kind, p, command_resolver=None):
|
||||
interval = p.get('interval_ms')
|
||||
interval = _int(200 if interval is None else interval, 'status interval_ms', 0, 10000)
|
||||
return {'attempts': attempts, 'interval_ms': interval}
|
||||
if kind == 'proactive':
|
||||
# The proactive cleanup: consume whatever the card announces (the
|
||||
if kind == 'proactive-drain':
|
||||
# The proactive drain: consume whatever the card announces (the
|
||||
# TERMINAL RESPONSE is `first` for the first command, `respond` for
|
||||
# the rest) and confirm the card is idle; `require` asserts what was
|
||||
# drained (at least one match - an empty drain fails it).
|
||||
out = {'respond': normalise_respond(p.get('respond') or {}),
|
||||
'attempts': _int(3 if p.get('attempts') is None else p.get('attempts'),
|
||||
'proactive attempts', 1, 1000),
|
||||
'proactive-drain attempts', 1, 1000),
|
||||
'interval_ms': _int(200 if p.get('interval_ms') is None
|
||||
else p.get('interval_ms'),
|
||||
'proactive interval_ms', 0, 10000)}
|
||||
'proactive-drain interval_ms', 0, 10000)}
|
||||
if p.get('first') is not None:
|
||||
out['first'] = normalise_respond(p['first'])
|
||||
req = p.get('require')
|
||||
if req is not None:
|
||||
if not isinstance(req, dict):
|
||||
raise ScriptError('proactive require must be an object')
|
||||
ctype, cname = _resolve_command(req.get('command'), 'proactive require',
|
||||
raise ScriptError('proactive-drain require must be an object')
|
||||
ctype, cname = _resolve_command(req.get('command'), 'proactive-drain require',
|
||||
command_resolver)
|
||||
qualifier = _check_spec(req.get('qualifier'), 'proactive require.qualifier')
|
||||
qualifier = _check_spec(req.get('qualifier'), 'proactive-drain require.qualifier')
|
||||
if qualifier and '?' not in qualifier['value'] and len(qualifier['value']) != 2:
|
||||
raise ScriptError('proactive require.qualifier must be one byte')
|
||||
raise ScriptError('proactive-drain require.qualifier must be one byte')
|
||||
out['require'] = {'type': ctype, 'name': cname, 'qualifier': qualifier}
|
||||
return out
|
||||
raise ScriptError('unknown action kind %r' % kind)
|
||||
@@ -398,11 +398,11 @@ def _normalise_check(check, kind, params):
|
||||
else:
|
||||
sw = {'mode': 'exact', 'value': '9000'}
|
||||
data = _check_spec(check.get('data'), 'check.data')
|
||||
if kind == 'proactive' and data is not None:
|
||||
# The proactive cleanup's result data is the drained command list, not
|
||||
if kind == 'proactive-drain' and data is not None:
|
||||
# The proactive drain's result data is the drained command list, not
|
||||
# a single response: a `data` check would be silently ignored, so it
|
||||
# is refused here (assert the drained commands with `require`).
|
||||
raise ScriptError('check.data is not used by the proactive action - '
|
||||
raise ScriptError('check.data is not used by the proactive drain action - '
|
||||
'assert what was drained with require, or the final '
|
||||
'status word with check.sw')
|
||||
por = check.get('por')
|
||||
|
||||
Reference in New Issue
Block a user