Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions tests/libs/video_test_config_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ class SkipRule: # pylint: disable=too-many-instance-attributes
format: Test suite format ('vvs', 'fluster', 'soothe')
drivers: List of GPU drivers to skip. Valid values:
'all', 'nvidia', 'nvk', 'intel', 'anv', 'amd', 'radv'
devices: List of Vulkan device IDs (VkPhysicalDeviceProperties
deviceID) to narrow the rule to specific hardware, since
one driver spans many generations (anv covers both DG2 and
GT1-class iGPUs). Hex strings with or without a '0x'
prefix, wildcards allowed ('56a*'). Empty matches every
device.
platforms: List of platforms to skip ('all', 'windows', 'linux')
Note: Platform filtering is defined but not enforced.
reproduction: Whether failure is consistent ('always', 'flaky')
Expand All @@ -87,6 +93,7 @@ class SkipRule: # pylint: disable=too-many-instance-attributes
test_type: str
format: str
drivers: List[str] = field(default_factory=lambda: ["all"])
devices: List[str] = field(default_factory=list)
platforms: List[str] = field(default_factory=lambda: ["all"])
reproduction: str = "always"
reason: str = ""
Expand All @@ -110,6 +117,7 @@ def _parse_skip_entry(entry: Dict[str, Any], test_type: str) -> SkipRule:
test_type=test_type,
format=entry.get('format', 'vvs'),
drivers=entry.get('drivers', ['all']),
devices=entry.get('devices', []),
platforms=entry.get('platforms', ['all']),
reproduction=entry.get('reproduction', 'always'),
reason=entry.get('reason', ''),
Expand Down Expand Up @@ -172,12 +180,34 @@ def load_skip_list(skip_list_path: Optional[str] = None) -> List[SkipRule]:
return skip_rules


def is_test_skipped(
def _device_matches(rule_devices: List[str], current_device: str) -> bool:
"""Check a rule's device list against the detected Vulkan device ID.

An empty list means the rule is not device-specific. IDs are compared
as bare lowercase hex so '0x56A0', '56a0' and the glob '56a*' all match
a detected '56a0'.
"""
if not rule_devices:
return True
if not current_device:
return False

def normalize(device_id: str) -> str:
device_id = device_id.strip().lower()
return device_id[2:] if device_id.startswith("0x") else device_id

detected = normalize(current_device)
return any(fnmatch.fnmatch(detected, normalize(d)) for d in rule_devices)


def is_test_skipped( # pylint: disable=too-many-arguments
test_name: str,
test_format: str,
skip_rules: List[SkipRule],
*,
current_driver: str = "all",
test_type: str = "decode"
test_type: str = "decode",
current_device: str = ""
) -> Optional[SkipRule]:
"""
Check if a test should be skipped based on the skip list.
Expand All @@ -188,6 +218,7 @@ def is_test_skipped(
- Test type matches the rule's type (decode/encode)
- Test format matches the rule's format
- Current driver is in the rule's drivers list (or rule has 'all')
- Current device ID is in the rule's devices list (or the list is empty)

Note: Test names are normalized by stripping decode_/encode_ prefixes
before matching against skip rules.
Expand All @@ -198,6 +229,8 @@ def is_test_skipped(
skip_rules: List of SkipRule objects to check against
current_driver: Current GPU driver name (default: 'all' to match all)
test_type: Type of the test ('decode', 'encode')
current_device: Current Vulkan device ID; rules with a 'devices'
list only match when it is one of them

Returns:
The matching SkipRule if the test is skipped, None otherwise
Expand Down Expand Up @@ -225,6 +258,10 @@ def is_test_skipped(
if "all" not in rule.drivers and current_driver not in rule.drivers:
continue

# Check device match, narrowing drivers that span generations
if not _device_matches(rule.devices, current_device):
continue

# All conditions match - test is skipped
return rule

Expand Down
74 changes: 42 additions & 32 deletions tests/libs/video_test_driver_detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,23 @@
class SystemInfo:
"""System information detected from test output."""
gpu_name: str = ""
vendor_id: str = ""
device_id: str = ""
driver_name: str = ""
driver_version: str = ""
os_name: str = ""

def get_header(self) -> str:
"""Generate header string: GPU Model / Driver Version / OS"""
"""Generate header string: GPU (vendor:device) / Driver Version / OS"""
parts = []
if self.gpu_name:
parts.append(self.gpu_name)
gpu_str = self.gpu_name
if self.vendor_id or self.device_id:
ids = ":".join(
i for i in [self.vendor_id, self.device_id] if i
)
gpu_str = f"{gpu_str} ({ids})"
parts.append(gpu_str)
if self.driver_name or self.driver_version:
driver_str = self.driver_name
if self.driver_version:
Expand All @@ -48,7 +56,8 @@ def get_header(self) -> str:

def is_empty(self) -> bool:
"""Check if system info has any data."""
return not any([self.gpu_name, self.driver_name,
return not any([self.gpu_name, self.vendor_id,
self.device_id, self.driver_name,
self.driver_version, self.os_name])


Expand Down Expand Up @@ -304,11 +313,23 @@ def get_vendor_name(vendor_id: int) -> str:
return vendor_names.get(vendor_id, f"Unknown (0x{vendor_id:04X})")


_SELECTED_DEVICE_RE = re.compile(
r'\*\*\* Selected Vulkan physical device with name:.*?\*\*\*',
re.IGNORECASE | re.DOTALL)


def _extract_field(output: str, pattern: str) -> str:
"""Extract a single field from combined output using a regex pattern."""
match = re.search(pattern, output, re.IGNORECASE)
return match.group(1).strip() if match else ""


def parse_system_info_from_output(stdout: str, stderr: str = "") -> SystemInfo:
"""
Parse full system information from test executable output.

Extracts GPU name, driver name, driver version, and OS info.
Extracts GPU name, vendor/device IDs, driver name, driver version,
and OS info.

Args:
stdout: Standard output from test executable
Expand All @@ -323,36 +344,25 @@ def parse_system_info_from_output(stdout: str, stderr: str = "") -> SystemInfo:
driver ID: 5, driver name: NVIDIA,
Num Decode Queues: 16, Num Encode Queues: 3 ***
"""
combined_output = stdout + "\n" + stderr
combined = stdout + "\n" + stderr
info = SystemInfo(os_name=get_os_info())

# Extract GPU name from "device with name: XXX,"
gpu_pattern = r'device with name:\s*([^,]+)'
gpu_match = re.search(gpu_pattern, combined_output, re.IGNORECASE)
if gpu_match:
info.gpu_name = gpu_match.group(1).strip()

# Extract driver name
driver_name_pattern = r'driver name:\s*([^,\n*]+)'
driver_match = re.search(driver_name_pattern, combined_output,
re.IGNORECASE)
if driver_match:
info.driver_name = driver_match.group(1).strip()

# Extract driver version if present (format varies by vendor)
# NVIDIA format: "driver version: 550.120"
# Mesa format: "driver info: Mesa 24.0.0"
version_pattern = r'driver version:\s*([^\n,*]+)'
version_match = re.search(version_pattern, combined_output, re.IGNORECASE)
if version_match:
info.driver_version = version_match.group(1).strip()
else:
# Try driver info pattern (Mesa)
driver_info_pattern = r'driver info:\s*([^\n,*]+)'
info_match = re.search(driver_info_pattern, combined_output,
re.IGNORECASE)
if info_match:
info.driver_version = info_match.group(1).strip()
# Rejected and skipped devices print the same "vendor ID:"/"device ID:"
# tokens, so all fields must come from the selected-device line.
selected = _SELECTED_DEVICE_RE.search(combined)
scope = selected.group(0) if selected else combined

info.gpu_name = _extract_field(scope, r'device with name:\s*([^,\n]+)')
info.vendor_id = _extract_field(scope, r'vendor ID:\s*([0-9a-fA-F]+)')
info.device_id = _extract_field(scope,
r'(?<!UU)device ID:\s*([0-9a-fA-F]+)')
info.driver_name = _extract_field(scope, r'driver name:\s*([^,\n*]+)')

# Driver version: try "driver version:" first, fall back to "driver info:"
info.driver_version = (
_extract_field(scope, r'driver version:\s*([^\n,*]+)')
or _extract_field(scope, r'driver info:\s*([^\n,*]+)')
)

return info

Expand Down
16 changes: 12 additions & 4 deletions tests/libs/video_test_framework_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ def check_resources(self, auto_download: bool = True,
"""
raise NotImplementedError("Subclasses must implement check_resources")

def cleanup_results(self, test_type: str = "test") -> None:
def cleanup_results(self, test_type: str = "test",
protected_files: Optional[List[Path]] = None) -> None:
"""Clean up output artifacts if keep_files is False and no failures."""
has_failures = any(
r.status in [VideoTestStatus.ERROR, VideoTestStatus.CRASH]
Expand Down Expand Up @@ -268,8 +269,11 @@ def cleanup_results(self, test_type: str = "test") -> None:
return

try:
resolved_protected = {p.resolve() for p in (protected_files or [])}
for item in self.results_dir.iterdir():
if item.is_file() and not item.name.endswith('_results.json'):
if (item.is_file()
and not item.name.endswith('_results.json')
and item.resolve() not in resolved_protected):
item.unlink()
elif item.is_dir():
shutil.rmtree(item)
Expand Down Expand Up @@ -331,7 +335,8 @@ def _count_skipped_tests(self, samples: list, test_format: str = "vvs",
for sample in samples:
skip_rule = is_test_skipped(
sample.name, test_format, self._skip_rules,
current_driver=self.current_driver, test_type=test_type
current_driver=self.current_driver, test_type=test_type,
current_device=self.system_info.device_id
)
if skip_rule is not None:
count += 1
Expand Down Expand Up @@ -481,6 +486,8 @@ def export_results_json(self, output_file: str, test_type: str) -> bool:
json.dump({
"system_info": {
"gpu_name": sys_info.gpu_name,
"vendor_id": sys_info.vendor_id,
"device_id": sys_info.device_id,
"driver_name": sys_info.driver_name,
"driver_version": sys_info.driver_version,
"os_name": sys_info.os_name,
Expand Down Expand Up @@ -828,7 +835,8 @@ def run_test_suite_base(self, test_configs: list,

skip_rule = is_test_skipped(
config.name, "vvs", self._skip_rules,
current_driver=self.current_driver, test_type=test_type
current_driver=self.current_driver, test_type=test_type,
current_device=self.system_info.device_id
)

if self._should_mask_as_skipped(skip_rule, result):
Expand Down
Loading
Loading