Skip to content

Commit 644cfdf

Browse files
Add NPU tests (New) (#2198)
* Add NPU provider (New) * Check RW permissions together, not separately * Replace find with in for string search * Add a job to check modinfo * Add the npu provider to CI * Add the npu provider to CI (paths) * Add tox.ini * remove the pytest import from tests * Remove type hints for python<3.10 compatibility * Remove assert_called_once for python<3.6 compatibility * Use OrderedDict for python<3.6 compatibility * Make it optional to specify the config file * Update according to the latest version of intel-npu-driver * Fix certification-status * Fix CI * Update providers/npu/bin/check_firmware_version.py Remove unreachable return Co-authored-by: Fernando Bravo <39527354+fernando79513@users.noreply.github.com> * Reformatting for less lines * Replace subprocess.run with subprocess.check_output * Print more information when fw not found * Add error message when fw version not found in file. * Run known failures always but return success * Fix formatting * Use packaging.version * Add a comment about the fw version regex * Fix formatting * Separate job template for known failures * Move the kernel version check into check_firmware_version.py * Adjust template summary of known failures. * Add a npu-umd-test detect job. * Remove old resource from test plan. * Move NPU tests into the base provider. --------- Co-authored-by: Fernando Bravo <39527354+fernando79513@users.noreply.github.com>
1 parent fcb29ab commit 644cfdf

13 files changed

Lines changed: 1060 additions & 11 deletions

providers/base/README.rst

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,27 @@ Base Provider Units
1313
###################
1414

1515
+-----------+-------------+-------------+-------------------+-------------+----------------+
16-
| 6lowpan | eeprom | hibernate | memory | self | tmp |
16+
| 6lowpan | eeprom | hibernate | memory | security | touchscreen |
1717
+-----------+-------------+-------------+-------------------+-------------+----------------+
18-
| acpi | esata | i2c | miscellanea | serial | ubuntucore |
18+
| acpi | esata | i2c | miscellanea | self | tmp |
1919
+-----------+-------------+-------------+-------------------+-------------+----------------+
20-
| audio | ethernet | image | mobilebroadband | smoke | usb |
20+
| audio | ethernet | image | mobilebroadband | serial | ubuntucore |
2121
+-----------+-------------+-------------+-------------------+-------------+----------------+
22-
| benchmarks| expresscard | info | monitor | snapd | virtualization |
22+
| benchmarks| expresscard | info | monitor | smoke | usb |
2323
+-----------+-------------+-------------+-------------------+-------------+----------------+
24-
| bluetooth | fingerprint | input | networking | socketcan | watchdog |
24+
| bluetooth | fingerprint | input | networking | snapd | virtualization |
2525
+-----------+-------------+-------------+-------------------+-------------+----------------+
26-
| camera_ | firewire | install | nvdimm | submission | wireless |
26+
| camera_ | firewire | install | npu | socketcan | watchdog |
2727
+-----------+-------------+-------------+-------------------+-------------+----------------+
28-
| canary | firmware | kernel-snap | oob-management | suspend | wwan |
28+
| canary | firmware | kernel-snap | nvdimm | submission | wireless |
2929
+-----------+-------------+-------------+-------------------+-------------+----------------+
30-
| codecs | fscrypt | keys | optical | tpm | |
30+
| codecs | fscrypt | keys | oob-management | suspend | wwan |
3131
+-----------+-------------+-------------+-------------------+-------------+----------------+
32-
| cpu | gadget | led | power-management | thunderbolt | |
32+
| cpu | gadget | led | optical | tpm | |
3333
+-----------+-------------+-------------+-------------------+-------------+----------------+
34-
| disk | gpio | location | rtc | touchpad | |
34+
| disk | gpio | location | power-management | thunderbolt | |
3535
+-----------+-------------+-------------+-------------------+-------------+----------------+
36-
| dock | graphics | mediacard | security | touchscreen | |
36+
| dock | graphics | mediacard | rtc | touchpad | |
3737
+-----------+-------------+-------------+-------------------+-------------+----------------+
3838

3939
.. _camera: units/camera/README.rst
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env python3
2+
import os
3+
from pathlib import Path
4+
5+
6+
def find_npu_device_path():
7+
base_sys_path = Path("/sys/class/accel")
8+
if not base_sys_path.is_dir():
9+
raise SystemExit("'{}' is not a directory.".format(base_sys_path))
10+
11+
for device_dir in base_sys_path.iterdir():
12+
try:
13+
# Check if the driver's name is 'intel_vpu'
14+
driver_path = device_dir / "device" / "driver"
15+
if "intel_vpu" in driver_path.readlink().name:
16+
device_path = Path("/dev/accel") / device_dir.name
17+
if device_path.exists():
18+
return device_path
19+
except (IOError, FileNotFoundError):
20+
# Ignore directories that don't match the expected structure
21+
continue
22+
23+
raise SystemExit("Could not find an Intel NPU device in /sys/class/accel.")
24+
25+
26+
def main():
27+
npu_device = find_npu_device_path()
28+
29+
# Check for read and write permissions
30+
has_readwrite_perm = os.access(npu_device, os.R_OK | os.W_OK)
31+
32+
if not has_readwrite_perm:
33+
raise SystemExit(
34+
"User lacks required permissions for {}".format(npu_device)
35+
)
36+
37+
38+
if __name__ == "__main__":
39+
main()
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/usr/bin/env python3
2+
import re
3+
import subprocess
4+
from pathlib import Path
5+
import platform
6+
from packaging import version
7+
8+
FIRMWARE_SEARCH_DIR = Path("/var/snap/intel-npu-driver/current/intel/vpu")
9+
10+
# Version string always starts with a date followed by an asterisk, the date is
11+
# in one of these two formats: YYYYMMDD or Mmm DD YYYY
12+
#
13+
# Examples of both formats:
14+
# - "20250925*MTL_CLIENT_SILICON-NVR+NN-deployment*2485cfeafeed591eaa9a320bfa\
15+
# e2407c1b83b29f*2485cfeafeed591eaa9a320bfae2407c1b83b29f*2485cfeafee"
16+
# - "Sep 25 2025*NPU40xx*build/ci/npu-fw-ci-ci_branch_UD202538_PTL_PV_npu_rel\
17+
# ease_25ww35-20250915_222036-29036-1-g2485cfeafee*2485cfeafeed591eaa9a\
18+
# 320bfae2407c1b83b29f"
19+
VERSION_PATTERN = re.compile(r"^(\d{8}\*|[A-Z][a-z]{2}\s+\d{1,2}\s+\d{4}\*).*")
20+
21+
22+
def check_kernel_version(required_version):
23+
current_version_str = platform.release()
24+
25+
current_version = version.Version(current_version_str.rsplit("-", 1)[0])
26+
required_version = version.Version(required_version.rsplit("-", 1)[0])
27+
28+
# Return True if current version is at least the required version
29+
return (
30+
current_version is not None
31+
and required_version is not None
32+
and current_version >= required_version
33+
)
34+
35+
36+
def get_active_firmware_line():
37+
result = subprocess.check_output(
38+
["journalctl", "--dmesg"], universal_newlines=True
39+
).splitlines()
40+
41+
matching_lines = [line for line in result if "Firmware: intel/vpu" in line]
42+
43+
if not matching_lines:
44+
raise SystemExit("No 'intel_vpu' firmware logs found in dmesg.")
45+
46+
return matching_lines[-1]
47+
48+
49+
def find_version_in_file(filepath):
50+
try:
51+
result = subprocess.check_output(
52+
["strings", filepath], universal_newlines=True
53+
)
54+
for line in result.splitlines():
55+
# Return the first match found
56+
if VERSION_PATTERN.match(line):
57+
return line
58+
except (subprocess.CalledProcessError, FileNotFoundError):
59+
# This can happen with corrupted files or if 'strings' isn't installed
60+
raise SystemExit(
61+
"`strings` is not installed or can't read "
62+
"the firmware file {}.".format(filepath)
63+
)
64+
65+
print("No version number found in the file {}".format(filepath))
66+
67+
68+
def main():
69+
if not check_kernel_version("6.8"):
70+
print(
71+
"Getting the firmware version out of dmesg is not supported "
72+
"on this system. Skipping..."
73+
)
74+
return
75+
76+
active_firmware_line = get_active_firmware_line()
77+
78+
if not FIRMWARE_SEARCH_DIR.is_dir():
79+
raise SystemExit("Firmware directory not found.")
80+
81+
found_driver_versions = []
82+
for filepath in FIRMWARE_SEARCH_DIR.iterdir():
83+
if filepath.is_file() and filepath.suffix == ".bin":
84+
driver_version = find_version_in_file(filepath)
85+
86+
if driver_version:
87+
if driver_version in active_firmware_line:
88+
print(
89+
"Test success: Loaded NPU firmware version matches a "
90+
"file from the snap. Version: {}, File: "
91+
"{}".format(driver_version, str(filepath))
92+
)
93+
return
94+
95+
found_driver_versions.append(driver_version)
96+
97+
raise SystemExit(
98+
"The loaded firmware does not match any version in the snap files.\n"
99+
"Loaded firmware: {}\n"
100+
"Versions found in the snap: {}".format(
101+
active_firmware_line,
102+
"\n".join(map(lambda e: " - " + e, found_driver_versions)),
103+
)
104+
)
105+
106+
107+
if __name__ == "__main__":
108+
main()
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import subprocess
4+
from collections import OrderedDict
5+
6+
7+
def print_as_resource(d):
8+
for k, v in d.items():
9+
print("{}: {}".format(k, v))
10+
print("")
11+
12+
13+
def get_extra_flags(category):
14+
extra_flags = []
15+
if category.startswith("ZeInit"):
16+
extra_flags.append("--ze-init-tests")
17+
18+
if "DmaHeap" in category:
19+
extra_flags.append("--dma-heap")
20+
21+
return extra_flags
22+
23+
24+
def main():
25+
config_path = os.environ.get("NPU_UMD_TEST_CONFIG") or "basic.yaml"
26+
27+
gtest_output = subprocess.check_output(
28+
["intel-npu-driver.npu-umd-test", "-l", "--config", config_path],
29+
universal_newlines=True,
30+
)
31+
32+
known_failures = (
33+
subprocess.check_output(
34+
["intel-npu-driver.known-failures"], universal_newlines=True
35+
)
36+
.strip()
37+
.splitlines()
38+
)
39+
40+
for line in gtest_output.strip().splitlines():
41+
if "." in line:
42+
is_known_failure = line in known_failures
43+
44+
category, test_name = line.split(".", 1)
45+
extra_flags = get_extra_flags(category)
46+
47+
records = OrderedDict()
48+
records["name"] = test_name
49+
records["category"] = category
50+
records["extra_flags"] = " ".join(extra_flags)
51+
records["known_failure"] = is_known_failure
52+
53+
print_as_resource(records)
54+
55+
56+
if __name__ == "__main__":
57+
main()

0 commit comments

Comments
 (0)