Files
scst/scripts/kunit/labels.py
T
Gleb Chesnokov a246e3fd2a kunit: Add composite runtime labels
Allow named sets of base labels and exact suite names in one TOML file.
Keep KUNIT_LABEL as the selection interface and combine overlapping sets
without running a suite twice. Include an IO set for CDB, UNMAP and VDISK.

Share expansion between source listing and runtime module selection.
Validate requested sets against the supplied module manifests before any
module operation, then load their owners and dependencies as before.
Reject namespace conflicts, nested sets and incomplete selections.

List descriptions and expanded suites without a build or loaded modules.
Allow an alternate file through --label-config, record selected definitions
in reports, and cover source listing, configuration and module ordering.
2026-10-14 16:30:27 +03:00

70 lines
3.3 KiB
Python

# SPDX-License-Identifier: GPL-2.0-only
"""Expand source or module labels and configured sets into whole suites."""
from pathlib import Path
import re
import tomllib
DEFAULT_LABEL_CONFIG = Path(__file__).with_name('labels.toml')
LABEL = re.compile(r'[A-Z][A-Z0-9_]*')
SUITE = re.compile(r'scst-[A-Za-z0-9_-]+')
def load_composites(path=DEFAULT_LABEL_CONFIG):
with path.open('rb') as source:
definitions = tomllib.load(source)
result = {}
for name, entry in definitions.items():
if not LABEL.fullmatch(name) or name == 'ALL' or not isinstance(entry, dict):
raise ValueError(f'{path}: invalid composite label {name!r}')
if entry.keys() - {'description', 'labels', 'suites'}:
raise ValueError(f'{path}: unknown fields in {name}')
description = entry.get('description', '')
if not isinstance(description, str) or '\n' in description or '\r' in description:
raise ValueError(f'{path}: {name}.description must be a single-line string')
for field, pattern in (('labels', LABEL), ('suites', SUITE)):
values = entry.get(field, [])
if not isinstance(values, list) or any(
not isinstance(value, str) or not pattern.fullmatch(value) for value in values):
raise ValueError(f'{path}: {name}.{field} must be an array of valid names')
labels, suites = entry.get('labels', []), entry.get('suites', [])
if not labels and not suites:
raise ValueError(f'{path}: composite label {name} is empty')
if invalid := set(labels) & (definitions.keys() | {'ALL'}):
raise ValueError(f'{path}: {name} must reference base labels only: {sorted(invalid)}')
result[name] = {'description': description, 'labels': labels, 'suites': suites}
return result
def expand_labels(suites, selected, composites):
"""Validate requested sets against this inventory and keep its suite order.
Unselected composites may refer to absent modules. A selected composite
must resolve completely, rather than silently running only part of it.
"""
base = set(suites.values())
if conflicts := base & composites.keys():
raise ValueError(f'Composite labels conflict with base labels: {sorted(conflicts)}')
if not selected or ('ALL' in selected and selected != ['ALL']):
raise ValueError('Select labels or ALL alone')
if selected == ['ALL']:
return list(suites)
if unknown := set(selected) - base - composites.keys():
raise ValueError(f'Unknown or unavailable KUnit labels: {sorted(unknown)}')
chosen = set()
for name in selected:
if name in base:
chosen.update(suite for suite, label in suites.items() if label == name)
continue
entry = composites[name]
if missing := set(entry['labels']) - base:
raise ValueError(f'{name}: base labels absent from this inventory: {sorted(missing)}')
if missing := set(entry['suites']) - suites.keys():
raise ValueError(f'{name}: suites absent from this inventory: {sorted(missing)}')
chosen.update(entry['suites'])
chosen.update(suite for suite, label in suites.items() if label in entry['labels'])
if not chosen:
raise ValueError('Selected labels contain no suites')
return [suite for suite in suites if suite in chosen]