diff options
-rwxr-xr-x | bin/boot_mode_test_snappy.py | 66 | ||||
-rwxr-xr-x | bin/booted_kernel_tests.py | 31 | ||||
-rwxr-xr-x | bin/disk_read_performance_test | 11 | ||||
-rwxr-xr-x | bin/evdev_touch_test.py | 75 | ||||
-rwxr-xr-x | bin/fde_tests.py | 5 | ||||
-rwxr-xr-x | bin/storage_test.py | 5 | ||||
-rw-r--r-- | units/disk/encryption.pxu | 6 | ||||
-rw-r--r-- | units/graphics/jobs.pxu | 20 | ||||
-rw-r--r-- | units/graphics/legacy.pxu | 15 | ||||
-rw-r--r-- | units/graphics/test-plan.pxu | 16 | ||||
-rw-r--r-- | units/kernel-snap/jobs.pxu | 23 | ||||
-rw-r--r-- | units/kernel-snap/test-plan.pxu | 4 | ||||
-rw-r--r-- | units/miscellanea/jobs.pxu | 1 | ||||
-rw-r--r-- | units/snappy/test-plan.pxu | 12 | ||||
-rw-r--r-- | units/suspend/suspend-graphics.pxu | 8 | ||||
-rw-r--r-- | units/suspend/suspend.pxu | 16 | ||||
-rw-r--r-- | units/touchscreen/jobs.pxu | 95 | ||||
-rw-r--r-- | units/touchscreen/packaging.pxu | 3 | ||||
-rw-r--r-- | units/touchscreen/test-plan.pxu | 20 | ||||
-rw-r--r-- | units/wireless/wifi-ap.pxu | 58 | ||||
-rw-r--r-- | units/wireless/wireless-connection-manual.pxu | 18 | ||||
-rw-r--r-- | units/wireless/wireless-connection-netplan.pxu | 16 |
22 files changed, 398 insertions, 126 deletions
diff --git a/bin/boot_mode_test_snappy.py b/bin/boot_mode_test_snappy.py index 46bed92..864be29 100755 --- a/bin/boot_mode_test_snappy.py +++ b/bin/boot_mode_test_snappy.py @@ -1,16 +1,22 @@ #!/usr/bin/env python3 -# Copyright 2018 Canonical Ltd. +# Copyright 2018-2019 Canonical Ltd. # Written by: # Jonathan Cave <jonathan.cave@canonical.com> +# Sylvain Pineau <sylvain.pineau@canonical.com> import io import os import re +import shutil import sys import subprocess as sp +import tempfile import yaml +from checkbox_support.parsers.kernel_cmdline import parse_kernel_cmdline +from checkbox_support.snap_utils.system import get_lk_bootimg_path + def fitdumpimage(filename): cmd = 'dumpimage -l {}'.format(filename) @@ -26,8 +32,9 @@ def fitdumpimage(filename): # from then on should get blocks of text describing the objects that were # combined in to the FIT image e.g. kernel, ramdisk, device tree - image_re = re.compile(r'(?:^\ Image)\ \d+\ \((\S+)\)$') - config_re = re.compile(r'^\ Default Configuration|^\ Configuration') + image_config_re = re.compile( + r'(?:^\ Image|Configuration)\ \d+\ \((\S+)\)$') + configuration_re = re.compile(r'^\ Default Configuration') objects = {} name = '' while True: @@ -36,16 +43,16 @@ def fitdumpimage(filename): if line == '': break # interested in storing image information - match = image_re.search(line) + match = image_config_re.search(line) if match: name = match.group(1) objects[name] = {} continue - # not interested in configurations - if config_re.search(line): + # not interested in the default configuration + if configuration_re.search(line): name = '' continue - # while in an image section store the info + # while in an image/config section store the info if name != '': entries = [s.strip() for s in line.split(':', 1)] objects[name][entries[0]] = entries[1] @@ -71,7 +78,7 @@ def main(): if not bootloader: raise SystemExit('ERROR: could not find name of bootloader') - if bootloader not in ('u-boot', 'grub'): + if bootloader not in ('u-boot', 'grub', 'lk'): raise SystemExit( 'ERROR: Unexpected bootloader name {}'.format(bootloader)) print('Bootloader is {}\n'.format(bootloader)) @@ -86,6 +93,8 @@ def main(): boot_objects = fitdumpimage(boot_kernel) for obj, attrs in boot_objects.items(): + if obj == 'conf': + continue print('Checking object {}'.format(obj)) if 'Sign value' not in attrs: raise SystemExit('ERROR: no sign value found for object') @@ -110,6 +119,47 @@ def main(): print('Secure Boot appears to be enabled on this system') + if bootloader == 'lk': + bootimg_path = get_lk_bootimg_path() + if bootimg_path == 'unknown': + raise SystemExit('ERROR: lk-boot-env not found') + + # XXX: Assuming FIT format + bootimg = os.path.basename(bootimg_path) + print('Parsing FIT image information ({})...\n'.format(bootimg)) + + with tempfile.TemporaryDirectory() as tmpdirname: + shutil.copy2(bootimg_path, tmpdirname) + boot_kernel = os.path.join(tmpdirname, bootimg) + boot_objects = fitdumpimage(boot_kernel) + + for obj, attrs in boot_objects.items(): + if obj != 'conf': + continue + print('Checking object {}'.format(obj)) + if 'Sign value' not in attrs: + raise SystemExit('ERROR: no sign value found for object') + print('Found "Sign value"') + if len(attrs['Sign value']) != 512: + raise SystemExit('ERROR: unexpected sign value size') + if all(s in attrs['Sign algo'] for s in ['sha256', 'rsa2048']): + print('Found expected signing algorithms') + else: + raise SystemExit( + 'ERROR: unexpected signing algorithms {}'.format( + attrs['Sign algo'])) + print() + + # check that all parts of the fit image have + snap_kernel = '/snap/{}/current/boot.img'.format(kernel) + snap_objects = fitdumpimage(snap_kernel) + if snap_objects != boot_objects: + raise SystemExit( + 'ERROR: boot kernel and current snap kernel do not match') + print('Kernel images in current snap and lk snapbootsel match\n') + + print('Secure Boot appears to be enabled on this system') + if bootloader == 'grub': cmd = 'mokutil --sb-state' print('+', cmd, flush=True) diff --git a/bin/booted_kernel_tests.py b/bin/booted_kernel_tests.py index ed54b25..bff9ff9 100755 --- a/bin/booted_kernel_tests.py +++ b/bin/booted_kernel_tests.py @@ -6,23 +6,15 @@ # Jonathan Cave <jonathan.cave@canonical.com> import hashlib +import os import sys from checkbox_support.snap_utils.system import get_kernel_snap -from checkbox_support.snap_utils.system import get_bootloader # 64kb buffer, hopefully suitable for all devices that might run this test BUF_SIZE = 65536 -def get_running_kernel_path(): - bootloader = get_bootloader() - if bootloader is None: - raise SystemExit('ERROR: failed to get bootloader') - path = '/boot/{}/kernel.img'.format(bootloader) - return path - - def get_snap_kernel_path(): kernel = get_kernel_snap() if kernel is None: @@ -42,15 +34,28 @@ def get_hash(path): return sha1.hexdigest() -def kernel_matches_current(): - rh = get_hash(get_running_kernel_path()) +def kernel_matches_current(booted_kernel_image): + rh = get_hash(booted_kernel_image) print('Running kernel hash:\n', rh, '\n') - sh = get_hash(get_snap_kernel_path()) + snap_kernel_image = get_snap_kernel_path() + print('Snap kernel image: {}'.format(snap_kernel_image)) + sh = get_hash(snap_kernel_image) print('Current kernel snap hash:\n', sh, '\n') if rh != sh: + print('ERROR: hashes do not match') return 1 + print('Hashes match') return 0 if __name__ == '__main__': - sys.exit(kernel_matches_current()) + if len(sys.argv) != 2: + raise SystemExit('ERROR: please specify the path to booted kernel') + booted_kernel_image = sys.argv[1] + + print('Supplied booted kernel image: {}'.format(booted_kernel_image)) + + if not os.path.exists(booted_kernel_image): + raise SystemExit('ERROR: invalid path to booted kernel supplied') + + sys.exit(kernel_matches_current(booted_kernel_image)) diff --git a/bin/disk_read_performance_test b/bin/disk_read_performance_test index 1cdea0d..972c5b6 100755 --- a/bin/disk_read_performance_test +++ b/bin/disk_read_performance_test @@ -13,10 +13,17 @@ for disk in $@; do disk_type=`udevadm info --name /dev/$disk --query property | grep "ID_BUS" | awk '{gsub(/ID_BUS=/," ")}{printf $1}'` dev_path=`udevadm info --name /dev/$disk --query property | grep "DEVPATH" | awk '{gsub(/DEVPATH=/," ")}{printf $1}'` + if [[ $dev_path =~ dm ]]; then + disk_type="devmapper" + fi if [[ $dev_path =~ nvme ]]; then disk_type="nvme" fi + if [[ $dev_path =~ mmc ]]; then + disk_type="mmc" + fi if [ -z "$disk_type" ]; then + echo "ERROR: disk type not recognized" exit 1 fi echo "INFO: $disk type is $disk_type" @@ -35,8 +42,10 @@ for disk in $@; do fi fi ;; + "devmapper" ) MIN_BUF_READ=$DEFAULT_BUF_READ;; "ide" ) MIN_BUF_READ=40;; - "nvme" ) MIN_BUF_READ=1000;; + "mmc" ) MIN_BUF_READ=$DEFAULT_BUF_READ;; + "nvme" ) MIN_BUF_READ=400;; * ) MIN_BUF_READ=$DEFAULT_BUF_READ;; esac echo "INFO: $disk_type: Using $MIN_BUF_READ MB/sec as the minimum throughput speed" diff --git a/bin/evdev_touch_test.py b/bin/evdev_touch_test.py new file mode 100755 index 0000000..5649701 --- /dev/null +++ b/bin/evdev_touch_test.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# This file is part of Checkbox. +# +# Copyright 2019 Canonical Ltd. +# +# Checkbox is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, +# as published by the Free Software Foundation. +# +# Checkbox is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. + +import argparse +import selectors +import time + +import evdev + +parser = argparse.ArgumentParser() +parser.add_argument("name", help='Touch device product name') +parser.add_argument("--timeout", type=int, default=10, help='timeout') +parser.add_argument( + "--xfingers", "-x", type=int, default=1, help="X-fingers tap event") +args = parser.parse_args() +start = time.time() + + +def check_timeout(): + if time.time() > start + args.timeout: + raise SystemExit("Event not detected") + + +selector = selectors.DefaultSelector() +for d in [evdev.InputDevice(path) for path in evdev.list_devices()]: + if d.name == args.name: + device = d + break +else: + raise SystemExit("Touch device not found!") +selector.register(device, selectors.EVENT_READ) + + +while True: + time.sleep(0.25) # sleep for 250 milliseconds + check_timeout() + for key, mask in selector.select(1): + dev = key.fileobj + for e in dev.read(): + tap = args.xfingers + if tap == 1: + if (e.type == 3 and e.code == 47 and e.value > 0): + raise SystemExit( + "Multitouch Event detected but Single was expected") + # type 1 is evdev.ecodes.EV_KEY + # code 330 is a BTN_TOUCH event + # value is a boolean, 1 means a PRESS, 0 a RELEASED event + if (e.type == 1 and e.code == 330 and e.value == 1): + print("SUCCESS:", e) + raise SystemExit + else: + # type 3 is evdev.ecodes.EV_ABS + # code 47 is a PRESS event + # value is the 0-indexed amount of simultaneous detected + # fingers + if (e.type == 3 and e.code == 47 and e.value == tap - 1): + print("SUCCESS:", e) + raise SystemExit + check_timeout() + else: + check_timeout() diff --git a/bin/fde_tests.py b/bin/fde_tests.py index 3008735..fd9ea14 100755 --- a/bin/fde_tests.py +++ b/bin/fde_tests.py @@ -28,7 +28,10 @@ def main(): base_mount = '/' if on_desktop else '/writable' # discover the underlying mount point for the encrypted part - cmd = 'findmnt {} -n -o SOURCE'.format(base_mount) + if on_desktop: + cmd = 'findmnt {} -n -o SOURCE'.format(base_mount) + else: + cmd = 'findfs LABEL=writable' print('+ {}'.format(cmd)) try: source = sp.check_output(cmd, shell=True).decode( diff --git a/bin/storage_test.py b/bin/storage_test.py index 3dbc87f..4f363bc 100755 --- a/bin/storage_test.py +++ b/bin/storage_test.py @@ -31,6 +31,9 @@ def find_largest_partition(device): blk_devs = [BlkDev(*p.strip().split()) for p in out.decode(sys.stdout.encoding).splitlines()] blk_devs[:] = [bd for bd in blk_devs if bd.type == 'part'] + if not blk_devs: + raise SystemExit( + 'ERROR: No partitions found on device {}'.format(device)) blk_devs.sort(key=lambda bd: int(bd.size)) return blk_devs[-1].name @@ -78,7 +81,7 @@ def run_bonnie(test_dir, user='root'): free = free_space(test_dir) print('{}MB of free space avaialble'.format(free)) if (force_mem_mb * 2) > free: - force_mem_mb = free / 2 + force_mem_mb = free / 4 print('Forcing memory setting to {}MB'.format(force_mem_mb)) cmd = 'bonnie++ -d {} -u {} -r {}'.format(test_dir, user, force_mem_mb) print('+', cmd, flush=True) diff --git a/units/disk/encryption.pxu b/units/disk/encryption.pxu index dea0637..b805a37 100644 --- a/units/disk/encryption.pxu +++ b/units/disk/encryption.pxu @@ -38,11 +38,11 @@ _purpose: partition. _steps: 1. Install the image and make sure it boots and you can log in. - 2. Turn the device off and upgrade/downgrade the BIOS + 2. Turn the device off and upgrade/downgrade the BIOS or modify Secure Boot state 3. Make sure the BIOS is set up properly (e.g. TPM enabled, UEFI boot mode) 4. Start the device _verification: Mark this test as "Passed" if the device cannot boot anymore. - Note: You must flash the BIOS back to the latest version and re-install the - image afterwards. + Note: You must flash the BIOS back to the latest version, re-enable Secure Boot + and re-install the image afterwards. diff --git a/units/graphics/jobs.pxu b/units/graphics/jobs.pxu index d5344e3..56f4158 100644 --- a/units/graphics/jobs.pxu +++ b/units/graphics/jobs.pxu @@ -254,28 +254,14 @@ _description: unit: template template-resource: graphics_card -template-filter: graphics_card.prime_gpu_offload == 'Off' -plugin: shell -category_id: com.canonical.plainbox::graphics -id: graphics/{index}_compiz_check_{product_slug} -requires: package.name == 'nux-tools' -command: - source graphics_env {driver} {index} - ! /usr/lib/nux/unity_support_test -c -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" -estimated_duration: 0.130 -_description: Check that {vendor} {product} hardware is able to run compiz -_summary: Test Compiz support for {vendor} {product} - -unit: template -template-resource: graphics_card plugin: shell category_id: com.canonical.plainbox::graphics -id: graphics/{index}_unity_support_{product_slug} +id: graphics/{index}_gl_support_{product_slug} requires: package.name == 'nux-tools' command: ! /usr/lib/nux/unity_support_test -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" estimated_duration: 0.131 -_description: Check that {vendor} {product} hardware is able to run Unity 3D -_summary: Test Unity 3D support for {vendor} {product} +_description: Check that {vendor} {product} hardware is able to run a desktop session (OpenGL) +_summary: Test OpenGL support for {vendor} {product} unit: template template-resource: graphics_card diff --git a/units/graphics/legacy.pxu b/units/graphics/legacy.pxu index 74153cc..6f18e02 100644 --- a/units/graphics/legacy.pxu +++ b/units/graphics/legacy.pxu @@ -155,21 +155,12 @@ _description: plugin: shell category_id: com.canonical.plainbox::graphics -id: graphics/compiz_check -requires: package.name == 'nux-tools' -command: ! /usr/lib/nux/unity_support_test -c -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" -estimated_duration: 0.130 -_summary: Test Compiz support -_description: Check that hardware is able to run compiz - -plugin: shell -category_id: com.canonical.plainbox::graphics -id: graphics/unity-support +id: graphics/gl_support requires: package.name == 'nux-tools' command: ! /usr/lib/nux/unity_support_test -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" estimated_duration: 0.131 -_description: Check that hardware is able to run Unity 3D -_summary: Test Unity 3D support +_description: Check that hardware is able to run a desktop session (OpenGL) +_summary: Test OpenGL support plugin: user-interact-verify category_id: com.canonical.plainbox::graphics diff --git a/units/graphics/test-plan.pxu b/units/graphics/test-plan.pxu index 4b8c7ec..1907209 100644 --- a/units/graphics/test-plan.pxu +++ b/units/graphics/test-plan.pxu @@ -37,7 +37,7 @@ include: graphics/xorg-process certification-status=blocker graphics/VESA_drivers_not_in_use certification-status=blocker graphics/1_driver_version_.* certification-status=blocker - graphics/1_compiz_check_.* certification-status=blocker + graphics/1_gl_support_.* certification-status=blocker graphics/1_minimum_resolution_.* suspend/1_resolution_before_suspend_.*_auto certification-status=blocker bootstrap_include: @@ -79,7 +79,7 @@ include: graphics/2_auto_switch_card_.* certification-status=blocker graphics/2_valid_opengl_renderer_.* certification-status=blocker graphics/2_driver_version_.* certification-status=blocker - graphics/2_compiz_check_.* certification-status=blocker + graphics/2_gl_support_.* certification-status=blocker graphics/2_minimum_resolution_.* suspend/2_resolution_before_suspend_.*_auto certification-status=blocker bootstrap_include: @@ -107,7 +107,7 @@ include: # or suspend/{{ index }}_suspend_after_switch_to_card_{{ product_slug }}_auto (two GPUs) suspend/1_suspend-time-check_.*_auto certification-status=non-blocker suspend/1_suspend-single-log-attach_.*_auto certification-status=non-blocker - suspend/1_compiz_check_after_suspend_.*_auto certification-status=blocker + suspend/1_gl_support_after_suspend_.*_auto certification-status=blocker suspend/1_driver_version_after_suspend_.*_auto certification-status=blocker suspend/1_resolution_after_suspend_.*_auto certification-status=blocker @@ -146,7 +146,7 @@ include: suspend/2_suspend_after_switch_to_card_.*_auto certification-status=blocker suspend/2_suspend-time-check_.*_auto certification-status=non-blocker suspend/2_suspend-single-log-attach_.*_auto certification-status=non-blocker - suspend/2_compiz_check_after_suspend_.*_auto certification-status=blocker + suspend/2_gl_support_after_suspend_.*_auto certification-status=blocker suspend/2_driver_version_after_suspend_.*_auto certification-status=blocker suspend/2_resolution_after_suspend_.*_auto certification-status=blocker @@ -174,7 +174,7 @@ include: graphics/1_maximum_resolution_.* certification-status=blocker graphics/1_glxgears_.* certification-status=blocker graphics/1_driver_version_.* certification-status=blocker - graphics/1_compiz_check_.* certification-status=blocker + graphics/1_gl_support_.* certification-status=blocker graphics/1_rotation_.* certification-status=blocker graphics/1_video_.* certification-status=blocker suspend/1_resolution_before_suspend_.*_auto certification-status=blocker @@ -191,7 +191,7 @@ include: graphics/2_valid_opengl_renderer_.* certification-status=blocker graphics/2_glxgears_.* certification-status=blocker graphics/2_driver_version_.* certification-status=blocker - graphics/2_compiz_check_.* certification-status=blocker + graphics/2_gl_support_.* certification-status=blocker graphics/2_rotation_.* certification-status=blocker graphics/2_video_.* certification-status=blocker bootstrap_include: @@ -207,7 +207,7 @@ include: power-management/lid certification-status=blocker power-management/lid_close certification-status=blocker power-management/lid_open certification-status=blocker - suspend/1_compiz_check_after_suspend_.*_auto certification-status=blocker + suspend/1_gl_support_after_suspend_.*_auto certification-status=blocker suspend/1_driver_version_after_suspend_.*_auto certification-status=blocker suspend/1_resolution_after_suspend_.*_auto certification-status=blocker suspend/1_display_after_suspend_.*_graphics certification-status=blocker @@ -221,7 +221,7 @@ _description: After suspend tests (discrete GPU, certification blockers only) include: suspend/2_resolution_before_suspend_.*_auto certification-status=blocker suspend/2_suspend_after_switch_to_card_.*_graphics certification-status=blocker - suspend/2_compiz_check_after_suspend_.*_auto certification-status=blocker + suspend/2_gl_support_after_suspend_.*_auto certification-status=blocker suspend/2_driver_version_after_suspend_.*_auto certification-status=blocker suspend/2_resolution_after_suspend_.*_auto certification-status=blocker suspend/2_display_after_suspend_.*_graphics certification-status=blocker diff --git a/units/kernel-snap/jobs.pxu b/units/kernel-snap/jobs.pxu index 5b573fd..151fd98 100644 --- a/units/kernel-snap/jobs.pxu +++ b/units/kernel-snap/jobs.pxu @@ -1,16 +1,33 @@ -id: kernel-snap/booted-kernel-matches-current +id: kernel-snap/force-kernel-extraction +category_id: kernel-snap +_summary: Kernel snap contains force-kernel-extraction flag +_description: + In Ubuntu Core 16 & Ubuntu Core 18 images for devices using Full Disk + Encryption it is necessary to inform snapd that when a kernel snap is + refreshed the kernel image should be extracted and placed in the boot + partition. +flags: simple fail-on-resource +command: echo "force-kernel-extraction file found in kernel snap" +imports: + from com.canonical.certification import ubuntu_core_features +requires: + ubuntu_core_features.force_kernel_extraction == 'True' + +unit: template +template-resource: bootloader +id: kernel-snap/booted-kernel-matches-current-{name} category_id: kernel-snap _summary: The booted kernel image matches image in current kernel snap _description: - On some Ubuntu Core deviecs it is necessary for the kernel image to be + On some Ubuntu Core devices it is necessary for the kernel image to be extracted from the kernel snap and placed in the boot partition (notably device using full disk encryption). This checks the images are in sync. plugin: shell user: root estimated_duration: 2.0 command: - booted_kernel_tests.py + booted_kernel_tests.py {booted_kernel_path} imports: from com.canonical.certification import ubuntu_core_features requires: diff --git a/units/kernel-snap/test-plan.pxu b/units/kernel-snap/test-plan.pxu index 7bd4dc8..ffaa5d1 100644 --- a/units/kernel-snap/test-plan.pxu +++ b/units/kernel-snap/test-plan.pxu @@ -13,7 +13,9 @@ unit: test plan _name: Automated Kernel Snap tests _description: Automated Kernel Snap tests for Ubuntu Core devices include: - kernel-snap/booted-kernel-matches-current + kernel-snap/booted-kernel-matches-current-.* +bootstrap_include: + bootloader id: kernel-snap-manual unit: test plan diff --git a/units/miscellanea/jobs.pxu b/units/miscellanea/jobs.pxu index 1641790..41c9253 100644 --- a/units/miscellanea/jobs.pxu +++ b/units/miscellanea/jobs.pxu @@ -160,6 +160,7 @@ id: miscellanea/secure_boot_mode_{gadget} _summary: Test that {gadget} Ubuntu Core system booted with Secure Boot active _description: Test to verify that the system booted with Secure Boot active. +user:root command: boot_mode_test_snappy.py {gadget} {kernel} diff --git a/units/snappy/test-plan.pxu b/units/snappy/test-plan.pxu index 9efba23..f688bbf 100644 --- a/units/snappy/test-plan.pxu +++ b/units/snappy/test-plan.pxu @@ -82,3 +82,15 @@ mandatory_include: snap bootstrap_include: model_assertion + +id: snappy-snap-automated-lightweight +unit: test plan +_name: Automated tests for snap command (lightweight version) +_description: + QA test plan that includes automated tests for the snap command for OEM classic devices. +estimated_duration: 1m +include: +exclude: + snappy/test-store.* +nested_part: + snappy-snap-automated diff --git a/units/suspend/suspend-graphics.pxu b/units/suspend/suspend-graphics.pxu index d4fe923..21effcc 100644 --- a/units/suspend/suspend-graphics.pxu +++ b/units/suspend/suspend-graphics.pxu @@ -115,7 +115,7 @@ template-filter: graphics_card.prime_gpu_offload == 'Off' template-engine: jinja2 plugin: shell category_id: com.canonical.plainbox::suspend -id: suspend/{{ index }}_compiz_check_after_suspend_{{ product_slug }}_auto +id: suspend/{{ index }}_gl_support_after_suspend_{{ product_slug }}_auto depends: {%- if gpu_count > "1" %} suspend/{{ index }}_suspend_after_switch_to_card_{{ product_slug }}_auto @@ -125,10 +125,10 @@ depends: requires: package.name == 'nux-tools' command: source graphics_env {{ driver }} {{ index }} - ! /usr/lib/nux/unity_support_test -c -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" + ! /usr/lib/nux/unity_support_test -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" estimated_duration: 0.130 -_description: Check that {{ product }} hardware is able to run compiz after suspend -_summary: Test Compiz support for {{ product }} after suspend +_description: Check that {{ product }} hardware is able to run a desktop session (OpenGL) +_summary: Test OpenGL support for {{ product }} after suspend unit: template template-resource: graphics_card diff --git a/units/suspend/suspend.pxu b/units/suspend/suspend.pxu index 35ba417..5d9bf08 100644 --- a/units/suspend/suspend.pxu +++ b/units/suspend/suspend.pxu @@ -2120,23 +2120,23 @@ template-resource: graphics_card template-filter: graphics_card.prime_gpu_offload == 'Off' plugin: shell category_id: com.canonical.plainbox::suspend -id: suspend/{index}_compiz_check_after_suspend_{product_slug} +id: suspend/{index}_gl_support_after_suspend_{product_slug} depends: suspend/{index}_suspend_after_switch_to_card_{product_slug} requires: package.name == 'nux-tools' -command: ! /usr/lib/nux/unity_support_test -c -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" +command: ! /usr/lib/nux/unity_support_test -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" estimated_duration: 0.130 -_description: Check that {product} hardware is able to run compiz after suspend -_summary: Test Compiz support for {product} after suspend +_description: Check that {product} hardware is able to run a desktop session (OpenGL) after suspend +_summary: Test OpenGL support for {product} after suspend plugin: shell category_id: com.canonical.plainbox::suspend -id: suspend/compiz_check_after_suspend +id: suspend/gl_support_after_suspend depends: suspend/suspend_advanced requires: package.name == 'nux-tools' -command: ! /usr/lib/nux/unity_support_test -c -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" +command: ! /usr/lib/nux/unity_support_test -p 2>&1 | ansi_parser | grep -e ":\(\s\+\)no$" -ie "error" estimated_duration: 0.130 -_description: Check that the hardware is able to run compiz after suspend -_summary: Test Compiz support after suspend +_description: Check that the hardware is able to run a desktop session (OpenGL) +_summary: Test OpenGL support after suspend unit: template template-resource: graphics_card diff --git a/units/touchscreen/jobs.pxu b/units/touchscreen/jobs.pxu index de55c5d..3a8a33a 100644 --- a/units/touchscreen/jobs.pxu +++ b/units/touchscreen/jobs.pxu @@ -197,3 +197,98 @@ command: {% endif %} exit $EXIT flags: also-after-suspend-manual + +unit: template +template-resource: device +template-filter: device.category == 'TOUCHSCREEN' +template-engine: jinja2 +template-unit: job +plugin: user-interact +category_id: com.canonical.plainbox::touchscreen +id: touchscreen/evdev/single-touch-tap-{{ product_slug }} +imports: from com.canonical.plainbox import manifest +requires: manifest.has_touchscreen == 'True' +estimated_duration: 10.0 +_description: + PURPOSE: + Validate that single-touch tap is properly detected + STEPS: + 1. Commence the test + 2. Tap the screen with one finger. + VERIFICATION: + If the tap is not detected the test will time out after 10 seconds. +user: root +command: evdev_touch_test.py '{{ product }}' -x 1 +flags: also-after-suspend + +unit: template +template-resource: device +template-filter: device.category == 'TOUCHSCREEN' +template-engine: jinja2 +template-unit: job +plugin: user-interact +category_id: com.canonical.plainbox::touchscreen +id: touchscreen/evdev/2-touch-tap-{{ product_slug }} +imports: from com.canonical.plainbox import manifest +requires: manifest.has_touchscreen == 'True' +estimated_duration: 10.0 +_description: + PURPOSE: + Validate that 2-touch tap is properly detected + STEPS: + 1. Commence the test + 2. Tap the screen with 2 fingers simultaneously. + VERIFICATION: + If the tap is not detected the test will time out after 10 seconds. +user: root +command: evdev_touch_test.py '{{ product }}' -x 2 +flags: also-after-suspend +after: touchscreen/evdev/single-touch-tap-{{ product_slug }} + +unit: template +template-resource: device +template-filter: device.category == 'TOUCHSCREEN' +template-engine: jinja2 +template-unit: job +plugin: user-interact +category_id: com.canonical.plainbox::touchscreen +id: touchscreen/evdev/3-touch-tap-{{ product_slug }} +imports: from com.canonical.plainbox import manifest +requires: manifest.has_touchscreen == 'True' +estimated_duration: 10.0 +_description: + PURPOSE: + Validate that 3-touch tap is properly detected + STEPS: + 1. Commence the test + 2. Tap the screen with 34 fingers simultaneously. + VERIFICATION: + If the tap is not detected the test will time out after 10 seconds. +user: root +command: evdev_touch_test.py '{{ product }}' -x 3 +flags: also-after-suspend +after: touchscreen/evdev/2-touch-tap-{{ product_slug }} + +unit: template +template-resource: device +template-filter: device.category == 'TOUCHSCREEN' +template-engine: jinja2 +template-unit: job +plugin: user-interact +category_id: com.canonical.plainbox::touchscreen +id: touchscreen/evdev/4-touch-tap-{{ product_slug }} +imports: from com.canonical.plainbox import manifest +requires: manifest.has_touchscreen == 'True' +estimated_duration: 10.0 +_description: + PURPOSE: + Validate that 4-touch tap is properly detected + STEPS: + 1. Commence the test + 2. Tap the screen with 4 fingers simultaneously. + VERIFICATION: + If the tap is not detected the test will time out after 10 seconds. +user: root +command: evdev_touch_test.py '{{ product }}' -x 4 +flags: also-after-suspend +after: touchscreen/evdev/3-touch-tap-{{ product_slug }} diff --git a/units/touchscreen/packaging.pxu b/units/touchscreen/packaging.pxu new file mode 100644 index 0000000..9c0b9d1 --- /dev/null +++ b/units/touchscreen/packaging.pxu @@ -0,0 +1,3 @@ +unit: packaging meta-data +os-id: debian +Depends: python3-evdev diff --git a/units/touchscreen/test-plan.pxu b/units/touchscreen/test-plan.pxu index cfe9907..f53e3dd 100644 --- a/units/touchscreen/test-plan.pxu +++ b/units/touchscreen/test-plan.pxu @@ -75,3 +75,23 @@ include: after-suspend-manual-touchscreen/multitouch-zoom certification-status=blocker after-suspend-manual-touchscreen/3-touch-tap certification-status=blocker after-suspend-manual-touchscreen/4-touch-tap certification-status=blocker + +id: touchscreen-evdev +unit: test plan +_name: Touchscreen evdev tests +_description: + Touchscreen evdev tests +include: + touchscreen/evdev.* +bootstrap_include: + device + +id: after-suspend-touchscreen-evdev +unit: test plan +_name: Touchscreen evdev tests +_description: + Touchscreen evdev tests +include: + after-suspend-touchscreen/evdev.* +bootstrap_include: + device diff --git a/units/wireless/wifi-ap.pxu b/units/wireless/wifi-ap.pxu index df25b50..335d505 100644 --- a/units/wireless/wifi-ap.pxu +++ b/units/wireless/wifi-ap.pxu @@ -1,6 +1,6 @@ unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_a_no_sta_{interface}_manual category_id: wifi_ap @@ -29,7 +29,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_a_no_sta_{interface}_auto category_id: wifi_ap @@ -71,7 +71,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_b_no_sta_{interface}_manual category_id: wifi_ap @@ -100,7 +100,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_b_no_sta_{interface}_auto category_id: wifi_ap @@ -142,7 +142,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_g_no_sta_{interface}_manual category_id: wifi_ap @@ -171,7 +171,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_g_no_sta_{interface}_auto category_id: wifi_ap @@ -213,7 +213,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_ad_no_sta_{interface}_manual category_id: wifi_ap @@ -242,7 +242,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_open_ad_no_sta_{interface}_auto category_id: wifi_ap @@ -284,7 +284,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_a_no_sta_{interface}_manual category_id: wifi_ap @@ -314,7 +314,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_a_no_sta_{interface}_auto category_id: wifi_ap @@ -357,7 +357,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_b_no_sta_{interface}_manual category_id: wifi_ap @@ -387,7 +387,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_b_no_sta_{interface}_auto category_id: wifi_ap @@ -430,7 +430,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_g_no_sta_{interface}_manual category_id: wifi_ap @@ -460,7 +460,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_g_no_sta_{interface}_auto category_id: wifi_ap @@ -503,7 +503,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_ad_no_sta_{interface}_manual category_id: wifi_ap @@ -533,7 +533,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_ad_no_sta_{interface}_auto category_id: wifi_ap @@ -576,7 +576,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_a_with_sta_{interface} category_id: wifi_ap @@ -615,7 +615,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_a_with_sta_{interface}_auto category_id: wifi_ap @@ -664,7 +664,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_b_with_sta_{interface} category_id: wifi_ap @@ -703,7 +703,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_b_with_sta_{interface}_auto category_id: wifi_ap @@ -752,7 +752,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_g_with_sta_{interface} category_id: wifi_ap @@ -791,7 +791,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_g_with_sta_{interface}_auto category_id: wifi_ap @@ -840,7 +840,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_ad_with_sta_{interface} category_id: wifi_ap @@ -879,7 +879,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_wpa_ad_with_sta_{interface}_auto category_id: wifi_ap @@ -928,7 +928,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_setup_wizard_{interface}_auto category_id: wifi_ap @@ -959,7 +959,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_across_reboot_{interface}_setup category_id: wifi_ap @@ -991,7 +991,7 @@ flags: preserve-locale noreturn autorestart unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_across_reboot_{interface}_setup_manual category_id: wifi_ap @@ -1025,7 +1025,7 @@ flags: preserve-locale noreturn unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_across_reboot_{interface}_check category_id: wifi_ap @@ -1057,7 +1057,7 @@ flags: preserve-locale unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wifi_ap_across_reboot_{interface}_check_manual category_id: wifi_ap diff --git a/units/wireless/wireless-connection-manual.pxu b/units/wireless/wireless-connection-manual.pxu index 9986578..13ff465 100644 --- a/units/wireless/wireless-connection-manual.pxu +++ b/units/wireless/wireless-connection-manual.pxu @@ -1,6 +1,6 @@ unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_open_ax_{interface} _summary: Connect to unencrypted 802.11ax Wi-Fi network on {interface} @@ -21,7 +21,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_open_ac_{interface} _summary: Connect to unencrypted 802.11ac Wi-Fi network on {interface} @@ -42,7 +42,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_open_bg_{interface} _summary: Connect to unencrypted 802.11b/g Wi-Fi network on {interface} @@ -63,7 +63,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_open_n_{interface} _summary: Connect to unencrypted 802.11n Wi-Fi network on {interface} @@ -84,7 +84,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_wpa_ax_{interface} _summary: Connect to WPA-encrypted 802.11ax Wi-Fi network on {interface} @@ -105,7 +105,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_wpa_ac_{interface} _summary: Connect to WPA-encrypted 802.11ac Wi-Fi network on {interface} @@ -126,7 +126,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_wpa_bg_{interface} _summary: Connect to WPA-encrypted 802.11b/g Wi-Fi network {interface} @@ -147,7 +147,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-unit: job id: wireless/wireless_connection_wpa_n_{interface} _summary: Connect to WPA-encrypted 802.11n Wi-Fi network on {interface} @@ -168,7 +168,7 @@ flags: preserve-locale also-after-suspend unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job plugin: shell diff --git a/units/wireless/wireless-connection-netplan.pxu b/units/wireless/wireless-connection-netplan.pxu index 956f83d..0483ac1 100644 --- a/units/wireless/wireless-connection-netplan.pxu +++ b/units/wireless/wireless-connection-netplan.pxu @@ -1,6 +1,6 @@ unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_open_ax_np_{{ interface }} @@ -24,7 +24,7 @@ requires: unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_open_ac_np_{{ interface }} @@ -49,7 +49,7 @@ requires: unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_open_bg_np_{{ interface }} @@ -72,7 +72,7 @@ flags: preserve-locale also-after-suspend also-after-suspend-manual unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_open_n_np_{{ interface }} @@ -95,7 +95,7 @@ flags: preserve-locale also-after-suspend also-after-suspend-manual unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_wpa_ax_np_{{ interface }} @@ -119,7 +119,7 @@ requires: unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_wpa_ac_np_{{ interface }} @@ -143,7 +143,7 @@ requires: unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_wpa_bg_np_{{ interface }} @@ -166,7 +166,7 @@ flags: preserve-locale also-after-suspend also-after-suspend-manual unit: template template-resource: device -template-filter: device.category == 'WIRELESS' +template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN' template-engine: jinja2 template-unit: job id: wireless/wireless_connection_wpa_n_np_{{ interface }} |