summaryrefslogtreecommitdiff
diff options
authorSylvain Pineau <sylvain.pineau@canonical.com>2019-08-20 15:35:58 +0200
committerSylvain Pineau <sylvain.pineau@canonical.com>2019-08-20 15:35:58 +0200
commitf70fc8e73824ddcefd3f5de171e87a5a6e1b6b50 (patch)
treebc59f2f813741aeba4ce041375ee63f238b4ae26
parent19561feae49044a9de817684b1d2dadea557675e (diff)
Import plainbox-provider-checkbox_0.49.0~rc1.orig.tar.gzupstream-0.49.0_rc1patched-0.49.0_rc1-1
-rwxr-xr-xbin/cpuid.py179
-rwxr-xr-xbin/edid_cycle.py53
-rwxr-xr-xbin/fan_reaction_test.py154
-rwxr-xr-xbin/graphics_env31
-rwxr-xr-xbin/hotkey_tests.py187
-rwxr-xr-xbin/network_configs16
-rwxr-xr-xbin/network_device_info.py525
-rwxr-xr-xbin/network_info96
-rwxr-xr-xbin/pm_test2
-rwxr-xr-xbin/rotation_test59
-rwxr-xr-xbin/storage_test213
-rwxr-xr-xbin/storage_test.py143
-rwxr-xr-xbin/wifi_client_test_netplan.py321
-rwxr-xr-xbin/wifi_nmcli_test.py100
-rw-r--r--data/edids/1280x1024.edidbin0 -> 256 bytes
-rw-r--r--data/edids/1920x1080.edidbin0 -> 256 bytes
-rw-r--r--data/edids/2560x1440.edidbin0 -> 256 bytes
-rwxr-xr-xmanage.py2
-rw-r--r--po/POTFILES.in1
-rw-r--r--units/audio/jobs.pxu34
-rw-r--r--units/bluetooth/jobs.pxu12
-rw-r--r--units/bluetooth/test-plan.pxu3
-rw-r--r--units/camera/jobs.pxu2
-rw-r--r--units/disk/jobs.pxu2
-rw-r--r--units/ethernet/jobs.pxu19
-rw-r--r--units/ethernet/test-plan.pxu6
-rw-r--r--units/graphics/test-plan.pxu113
-rw-r--r--units/info/jobs.pxu11
-rw-r--r--units/info/test-plan.pxu1
-rw-r--r--units/keys/test-plan.pxu4
-rw-r--r--units/mediacard/jobs.pxu14
-rw-r--r--units/mediacard/test-plan.pxu12
-rw-r--r--units/miscellanea/jobs.pxu18
-rw-r--r--units/miscellanea/test-plan.pxu2
-rw-r--r--units/monitor/jobs.pxu86
-rw-r--r--units/monitor/test-plan.pxu168
-rw-r--r--units/networking/jobs.pxu3
-rw-r--r--units/networking/test-plan.pxu1
-rw-r--r--units/optical/jobs.pxu2
-rw-r--r--units/optical/test-plan.pxu23
-rw-r--r--units/suspend/suspend-graphics.pxu37
-rw-r--r--units/thunderbolt/jobs.pxu30
-rw-r--r--units/thunderbolt/test-plan.pxu51
-rw-r--r--units/touchpad/test-plan.pxu14
-rw-r--r--units/touchscreen/jobs.pxu5
-rw-r--r--units/touchscreen/test-plan.pxu43
-rw-r--r--units/usb/test-plan.pxu1
-rw-r--r--units/usb/usb-c.pxu1
-rw-r--r--units/usb/usb.pxu2
-rw-r--r--units/wireless/jobs.pxu19
-rw-r--r--units/wireless/manifest.pxu4
-rw-r--r--units/wireless/wireless-connection-netplan.pxu139
52 files changed, 2106 insertions, 858 deletions
diff --git a/bin/cpuid.py b/bin/cpuid.py
new file mode 100755
index 0000000..a434309
--- /dev/null
+++ b/bin/cpuid.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python3
+
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Anders Høst
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# Modifications: 2019 Jeffrey Lane (jeffrey.lane@canonical.com)
+
+import ctypes
+import os
+import platform
+import sys
+from ctypes import (c_uint32, c_int, c_long, c_ulong, c_size_t, c_void_p,
+ POINTER, CFUNCTYPE)
+from subprocess import check_output
+
+# Posix x86_64:
+# Three first call registers : RDI, RSI, RDX
+# Volatile registers : RAX, RCX, RDX, RSI, RDI, R8-11
+
+# Windows x86_64:
+# Three first call registers : RCX, RDX, R8
+# Volatile registers : RAX, RCX, RDX, R8-11
+
+# cdecl 32 bit:
+# Three first call registers : Stack (%esp)
+# Volatile registers : EAX, ECX, EDX
+
+_POSIX_64_OPC = [
+ 0x53, # push %rbx
+ 0x89, 0xf0, # mov %esi,%eax
+ 0x89, 0xd1, # mov %edx,%ecx
+ 0x0f, 0xa2, # cpuid
+ 0x89, 0x07, # mov %eax,(%rdi)
+ 0x89, 0x5f, 0x04, # mov %ebx,0x4(%rdi)
+ 0x89, 0x4f, 0x08, # mov %ecx,0x8(%rdi)
+ 0x89, 0x57, 0x0c, # mov %edx,0xc(%rdi)
+ 0x5b, # pop %rbx
+ 0xc3 # retq
+]
+
+_CDECL_32_OPC = [
+ 0x53, # push %ebx
+ 0x57, # push %edi
+ 0x8b, 0x7c, 0x24, 0x0c, # mov 0xc(%esp),%edi
+ 0x8b, 0x44, 0x24, 0x10, # mov 0x10(%esp),%eax
+ 0x8b, 0x4c, 0x24, 0x14, # mov 0x14(%esp),%ecx
+ 0x0f, 0xa2, # cpuid
+ 0x89, 0x07, # mov %eax,(%edi)
+ 0x89, 0x5f, 0x04, # mov %ebx,0x4(%edi)
+ 0x89, 0x4f, 0x08, # mov %ecx,0x8(%edi)
+ 0x89, 0x57, 0x0c, # mov %edx,0xc(%edi)
+ 0x5f, # pop %edi
+ 0x5b, # pop %ebx
+ 0xc3 # ret
+]
+
+is_64bit = ctypes.sizeof(ctypes.c_voidp) == 8
+
+CPUIDS = {
+ "Amber Lake": ['0x806e9'],
+ "AMD EPYC": ['0x800f12'],
+ "AMD Opteron 6100": ['0x100f91'],
+ "AMD ROME": ['0x830f10'],
+ "Broadwell": ['0x4067', '0x306d4', '0x5066', '0x406f'],
+ "Canon Lake": ['0x6066'],
+ "Cascade Lake": ['0x50655', '0x50656', '0x50657'],
+ "Coffee Lake": ['0x806ea', '0x906ea', '0x906eb', '0x906ec'],
+ "Haswell": ['0x306c', '0x4065', '0x4066', '0x306f'],
+ "Ice Lake": ['0x706e'],
+ "Ivy Bridge": ['0x306a', '0x306e'],
+ "Kaby Lake": ['0x806e9', '0x906e9'],
+ "Knights Landing": ['0x5067'],
+ "Knights Mill": ['0x8065'],
+ "Nehalem": ['0x106a', '0x106e5', '0x206e'],
+ "Pineview": ['0x106ca'],
+ "Penryn": ['0x1067a'],
+ "Sandy Bridge": ['0x206a', '0x206d6', '0x206d7'],
+ "Skylake": ['0x406e3', '0x506e3', '0x50654', '0x50652'],
+ "Westmere": ['0x2065', '0x206c', '0x206f'],
+ "Whisky Lake": ['0x806eb', '0x806ec'],
+ }
+
+
+class CPUID_struct(ctypes.Structure):
+ _fields_ = [(r, c_uint32) for r in ("eax", "ebx", "ecx", "edx")]
+
+
+class CPUID(object):
+ def __init__(self):
+ if platform.machine() not in ("AMD64", "x86_64", "x86", "i686"):
+ print("ERROR: Only available for x86")
+ sys.exit(1)
+
+ opc = _POSIX_64_OPC if is_64bit else _CDECL_32_OPC
+
+ size = len(opc)
+ code = (ctypes.c_ubyte * size)(*opc)
+
+ self.libc = ctypes.cdll.LoadLibrary(None)
+ self.libc.valloc.restype = ctypes.c_void_p
+ self.libc.valloc.argtypes = [ctypes.c_size_t]
+ self.addr = self.libc.valloc(size)
+ if not self.addr:
+ print("ERROR: Could not allocate memory")
+ sys.exit(1)
+
+ self.libc.mprotect.restype = c_int
+ self.libc.mprotect.argtypes = [c_void_p, c_size_t, c_int]
+ ret = self.libc.mprotect(self.addr, size, 1 | 2 | 4)
+ if ret != 0:
+ print("ERROR: Failed to set RWX")
+ sys.exit(1)
+
+ ctypes.memmove(self.addr, code, size)
+
+ func_type = CFUNCTYPE(None, POINTER(CPUID_struct), c_uint32, c_uint32)
+ self.func_ptr = func_type(self.addr)
+
+ def __call__(self, eax, ecx=0):
+ struct = CPUID_struct()
+ self.func_ptr(struct, eax, ecx)
+ return struct.eax, struct.ebx, struct.ecx, struct.edx
+
+ def __del__(self):
+ # Seems to throw exception when the program ends and
+ # libc is cleaned up before the object?
+ self.libc.free.restype = None
+ self.libc.free.argtypes = [c_void_p]
+ self.libc.free(self.addr)
+
+
+def main():
+ cpuid = CPUID()
+ cpu = cpuid(1)
+
+ # Lets play Guess The CPU!
+ # First lets get the name from /proc/cpuinfo
+ cpu_data = check_output('lscpu', universal_newlines=True).split('\n')
+ for line in cpu_data:
+ if line.startswith('Model name:'):
+ print("CPU Model: %s" % line.split(':')[1].lstrip())
+
+ my_id = (hex(cpu[0]))
+ complete = False
+ for key in CPUIDS.keys():
+ for value in CPUIDS[key]:
+ if value in my_id:
+ print("CPUID: %s which appears to be a %s processor" %
+ (my_id, key))
+ complete = True
+
+ if not complete:
+ print("Unable to determine CPU Family for this CPUID: %s" % my_id)
+ return 1
+ else:
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/bin/edid_cycle.py b/bin/edid_cycle.py
new file mode 100755
index 0000000..7e3c97e
--- /dev/null
+++ b/bin/edid_cycle.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+"""
+This program tests whether the system changes the resolution automatically
+when supplied with a new EDID information.
+
+To run the test RaspberryPi equipped with a HDMI->CSI-2 bridge is needed. See
+here for details:
+https://docs.google.com/document/d/1kjgaazt2IMskn_HPjN7adXYx1O5zXc39DRayZ0PYh9Y
+
+The command-line argument for the program is the address of the RaspberryPi
+Host (optionally with a username), e.g.: pi@192.168.1.100
+"""
+import re
+import subprocess
+import sys
+import time
+
+
+def check_resolution():
+ output = subprocess.check_output('xdpyinfo')
+ for line in output.decode(sys.stdout.encoding).splitlines():
+ if 'dimensions' in line:
+ match = re.search('(\d+)x(\d+)\ pixels', line)
+ if match and len(match.groups()) == 2:
+ return '{}x{}'.format(*match.groups())
+
+
+def change_edid(host, edid_file):
+ with open(edid_file, 'rb') as f:
+ cmd = ['ssh', host, 'v4l2-ctl', '--set-edid=file=-,format=raw',
+ '--fix-edid-checksums']
+ subprocess.check_output(cmd, input=f.read())
+
+
+def main():
+ if len(sys.argv) != 2:
+ raise SystemExit('Usage: {} user@edid-host'.format(sys.argv[0]))
+ failed = False
+ for res in ['2560x1440', '1920x1080', '1280x1024']:
+ print('changing EDID to {}'.format(res))
+ change_edid(sys.argv[1], '{}.edid'.format(res))
+ time.sleep(1)
+ print('checking resolution... ', end='')
+ actual_res = check_resolution()
+ if actual_res != res:
+ print('FAIL, got {} instead'.format(actual_res))
+ failed = True
+ else:
+ print('PASS')
+ return failed
+
+if __name__ == '__main__':
+ raise SystemExit(main())
diff --git a/bin/fan_reaction_test.py b/bin/fan_reaction_test.py
new file mode 100755
index 0000000..3ae6362
--- /dev/null
+++ b/bin/fan_reaction_test.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+# Copyright 2019 Canonical Ltd.
+# Written by:
+# Maciej Kisielewski <maciej.kisielewski@canonical.com>
+#
+# This 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.
+#
+# This file 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 this file. If not, see <http://www.gnu.org/licenses/>.
+"""
+This program checks if the system's fans react to the CPU load applied.
+"""
+
+import glob
+import hashlib
+import multiprocessing
+import os
+import random
+import time
+
+
+class FanMonitor:
+ """Device that reports fan RPM or something correlating to that."""
+ def __init__(self):
+ """Use heuristics to find something that we can read."""
+ self._fan_paths = glob.glob('/sys/class/hwmon/hwmon*/fan*_input')
+ if not self._fan_paths:
+ print('Fan monitoring interface not found in SysFS')
+ raise SystemExit(0)
+ def get_rpm(self):
+ result = {}
+ for p in self._fan_paths:
+ with open(p, 'rt') as f:
+ fan_mon_name = os.path.relpath(p, '/sys/class/hwmon')
+ result[fan_mon_name] = int(f.read())
+ return result
+ def get_average_rpm(self, period):
+ acc = self.get_rpm()
+ for i in range(period):
+ time.sleep(1)
+ rpms = self.get_rpm()
+ for k, v in acc.items():
+ acc[k] += rpms[k]
+ for k, v in acc.items():
+ acc[k] /= period + 1
+ return acc
+
+
+class Stressor:
+ def __init__(self, thread_count=None):
+ """Prepare the stressor."""
+ if thread_count is None:
+ thread_count = multiprocessing.cpu_count()
+ print("Found {} CPU(s) in the system".format(thread_count))
+ print("Will use #{} thread(s)".format(thread_count))
+ self._thread_count = thread_count
+ self._procs = []
+
+ def start(self):
+ """Start stress processes in the background."""
+ for n in range(self._thread_count):
+ self._procs.append(
+ multiprocessing.Process(target=self._stress_fun))
+ self._procs[-1].start()
+
+ def stop(self):
+ """Stop all running stress processes."""
+ for proc in self._procs:
+ proc.terminate()
+ proc.join()
+ self._procs = []
+
+ def _stress_fun(self):
+ """Actual stress function."""
+ # generate some random data
+ data = bytes(random.getrandbits(8) for _ in range(1024))
+ hasher = hashlib.sha256()
+ hasher.update(data)
+ while True:
+ new_digest = hasher.digest()
+ # use the newly obtained digest as the new data to the hasher
+ hasher.update(new_digest)
+
+def main():
+ """Entry point."""
+
+ fan_mon = FanMonitor()
+ stressor = Stressor()
+
+ print("Precooling for 10s - no stress load")
+ time.sleep(10)
+ print("Measuring baseline fan speed")
+ baseline_rpm = fan_mon.get_average_rpm(5)
+ print("Launching stressor for 120s")
+ stressor.start()
+ for cycle in range(120):
+ print("Cycle #{}, RPM={}".format(cycle, fan_mon.get_rpm()))
+ time.sleep(1)
+ print("Measuring an average fan speed over 5s")
+ stress_rpm = fan_mon.get_average_rpm(5)
+ print("Stopping stressor, waiting for 60s for system to cool off")
+ stressor.stop()
+ for cycle in range(60):
+ print("Cycle #{}, RPM={}".format(cycle, fan_mon.get_rpm()))
+ time.sleep(1)
+ print("Measuring an average fan speed over 5s")
+ end_rpm = fan_mon.get_average_rpm(5)
+
+ had_a_fan_spinning = any((rpm > 0 for rpm in stress_rpm.values()))
+ rpm_rose_during_stress = False
+ rpm_dropped_during_cooling = False
+ for fan_mon in sorted(baseline_rpm.keys()):
+ if baseline_rpm[fan_mon]:
+ stress_delta = (stress_rpm[fan_mon] / baseline_rpm[fan_mon])
+ else:
+ stress_delta = 999 if stress_rpm[fan_mon] > 0 else 0
+ # if any of the fans raised rpms - similar to any()
+ if not rpm_rose_during_stress:
+ # XXX: this checks only if the rpms rose, not by how much
+ # we may want to introduce a threshold later on
+ rpm_rose_during_stress = stress_delta > 0
+ if not rpm_dropped_during_cooling:
+ rpm_dropped_during_cooling = (
+ end_rpm[fan_mon] < stress_rpm[fan_mon])
+
+ print("{} RPM:".format(fan_mon))
+ print(" baseline : {:.2f}".format(baseline_rpm[fan_mon]))
+ print(" during stress : {:.2f} ({:.2f}% of baseline)".format(
+ stress_rpm[fan_mon], stress_delta * 100))
+ print(" after stress : {:.2f}".format(end_rpm[fan_mon]))
+ if not had_a_fan_spinning:
+ print("The system had no fans spinning during the test")
+ return 0
+ if rpm_rose_during_stress:
+ print("RPM rose during the stress.")
+ else:
+ print("RPM did not rise during the stress!")
+ if rpm_dropped_during_cooling:
+ print("RPM dropped after the stress.")
+ else:
+ print("RPM did not drop after the stress!")
+ # inverse logic, returning True would mean return code of 1
+ if not (rpm_rose_during_stress and rpm_dropped_during_cooling):
+ raise SystemExit("Fans did not react to stress expectedly")
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/graphics_env b/bin/graphics_env
index 89c761d..19385a5 100755
--- a/bin/graphics_env
+++ b/bin/graphics_env
@@ -8,26 +8,23 @@
DRIVER=$1
INDEX=$2
-UBUNTU_CODENAME=`lsb_release -c | awk {'print $2'}`
-# We only want to set the DRI_PRIME env variable on Xenial (16.04) systems
-# with more than 1 GPU running the amdgpu/radeon drivers.
+# We only want to set the DRI_PRIME env variable on systems with more than
+# 1 GPU running the amdgpu/radeon drivers.
if [[ $DRIVER == "amdgpu" || $DRIVER == "radeon" ]]; then
- if [[ $UBUNTU_CODENAME == "xenial" ]]; then
- NB_GPU=`udev_resource -l VIDEO | grep -oP -m1 '\d+'`
- if [ $NB_GPU -gt 1 ]; then
- if [ $INDEX -gt 1 ]; then
- # See https://wiki.archlinux.org/index.php/PRIME
- echo "Setting up PRIME GPU offloading for AMD discrete GPU"
- if ! cat /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log 2>&1 | grep -q DRI3; then
- PROVIDER_ID=`xrandr --listproviders | grep "Sink Output" | awk {'print $4'} | tail -1`
- SINK_ID=`xrandr --listproviders | grep "Source Output" | awk {'print $4'} | tail -1`
- xrandr --setprovideroffloadsink ${PROVIDER_ID} ${SINK_ID}
- fi
- export DRI_PRIME=1
- else
- export DRI_PRIME=
+ NB_GPU=`udev_resource -l VIDEO | grep -oP -m1 '\d+'`
+ if [ $NB_GPU -gt 1 ]; then
+ if [ $INDEX -gt 1 ]; then
+ # See https://wiki.archlinux.org/index.php/PRIME
+ echo "Setting up PRIME GPU offloading for AMD discrete GPU"
+ if ! cat /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log 2>&1 | grep -q DRI3; then
+ PROVIDER_ID=`xrandr --listproviders | grep "Sink Output" | awk {'print $4'} | tail -1`
+ SINK_ID=`xrandr --listproviders | grep "Source Output" | awk {'print $4'} | tail -1`
+ xrandr --setprovideroffloadsink ${PROVIDER_ID} ${SINK_ID}
fi
+ export DRI_PRIME=1
+ else
+ export DRI_PRIME=
fi
fi
fi
diff --git a/bin/hotkey_tests.py b/bin/hotkey_tests.py
index 2a9282b..f1b3560 100755
--- a/bin/hotkey_tests.py
+++ b/bin/hotkey_tests.py
@@ -15,6 +15,20 @@
#
# You should have received a copy of the GNU General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
+"""
+This program tests whether the system properly reacts to hotkey presses.
+
+To inject the keypresses /dev/input/ devices are used.
+
+For particular scenarios see "check_*" methods of the HotKeyTesting class.
+
+TODO: Create one virtual input device using uinput.
+ Heuristic for picking the device to write to is not optimal, and on some
+ systems the fake keypresses are not registered. Having "own" input
+ device could improve reliability/portability.
+"""
+
+import contextlib
import datetime
import enum
import os
@@ -286,6 +300,7 @@ class KeyCodes(enum.Enum):
'/': KeyCodes.KEY_SLASH,
' ': KeyCodes.KEY_SPACE,
'-': KeyCodes.KEY_MINUS,
+ '.': KeyCodes.KEY_DOT,
}
if c in obvious_keys.keys():
return obvious_keys[c]
@@ -297,30 +312,48 @@ class KeyCodes(enum.Enum):
'One does not simply convert {} to a keycode'.format(c))
-def guess_kb_dev_path():
- """
- Guess the path of the keyboard device that we can send events to.
- """
- # most intel-based devices I tested had platform-i8042-serio-0-event-kbd
- # device, so let's use that if it is present
- intel_generic = '/dev/input/by-path/platform-i8042-serio-0-event-kbd'
-
- if os.path.exists(intel_generic):
- return intel_generic
- raise SystemExit("Couldn't guess a proper keyboard device")
+class VolumeChange:
+ def __init__(self):
+ self.before = 0
+ self.after = 0
+ self.mute_before = None
+ self.mute_after = None
class FauxKeyboard():
- def __init__(self, dev_path=guess_kb_dev_path()):
- self.kb_dev_file = open(dev_path, 'wb')
+ def __init__(self):
+ base = '/dev/input/by-path'
+ all_devs = [
+ os.path.join(base, dev) for dev in sorted(os.listdir(base))]
+ kbd_devs = [dev for dev in all_devs if dev.endswith('kbd')]
+ event_devs = [dev for dev in all_devs if dev.endswith('event-mouse')]
+ self.kb_dev_file = None
+ self.event_dev_file = None
+ if not kbd_devs:
+ raise SystemExit(
+ "Could not connect to existing keyboard connection. "
+ "Is keyboard plugged in?")
+ if not event_devs:
+ raise SystemExit(
+ "Could not connect to existing mouse connection. "
+ "Is mouse plugged in?")
+ self.kb_dev_file = open(kbd_devs[0], 'wb')
+ self.event_dev_file = open(event_devs[0], 'wb')
self.event_struct = struct.Struct('llHHi')
def __del__(self):
- self.kb_dev_file.close()
+ if self.kb_dev_file:
+ self.kb_dev_file.close()
+ if self.event_dev_file:
+ self.event_dev_file.close()
def type_text(self, text):
for c in text:
- self.press_key(KeyCodes.from_char(c))
+ if c == '>':
+ self.press_key(KeyCodes.KEY_DOT, {'shift'})
+ continue
+ modifiers = {'shift'} if c.isupper() else set()
+ self.press_key(KeyCodes.from_char(c), modifiers)
def press_key(self, key_code, modifiers=set(), repetitions=1, delay=0.05):
# simple key press actions contains four events:
@@ -330,6 +363,12 @@ class FauxKeyboard():
# EV_KEY, {KEY_CODE}, 0
# XXX: ATM there's no distinction between left and right modifiers
assert(repetitions >= 0)
+ # sending "special" codes (like media control ones) to a general kbd
+ # device doesn't work, so we have to send them to the event-mouse one
+ SPECIAL_CODES = [
+ KeyCodes.KEY_PLAYPAUSE,
+ ]
+ use_special = key_code in SPECIAL_CODES
while repetitions:
if not modifiers.issubset({'alt', 'ctrl', 'shift', 'meta'}):
raise SystemExit('Unknown modifier')
@@ -347,8 +386,12 @@ class FauxKeyboard():
mod_code = KeyCodes['KEY_LEFT{}'.format(mod.upper())].value
data += self.event_struct.pack(0, 10, 1, mod_code, 0)
data += self.event_struct.pack(0, 10, 0, 0, 0)
- self.kb_dev_file.write(data)
- self.kb_dev_file.flush()
+ if use_special:
+ self.event_dev_file.write(data)
+ self.event_dev_file.flush()
+ else:
+ self.kb_dev_file.write(data)
+ self.kb_dev_file.flush()
time.sleep(delay)
repetitions -= 1
@@ -358,9 +401,9 @@ class HotKeyTesting:
def __init__(self):
self.kb = FauxKeyboard()
- def check_volume_media_keys(self):
+ def check_volume_up(self):
"""
- Check if the volume media keys have an effect on ALSA
+ Check if the volume up key has an effect on ALSA
"""
# if the volume is already on max, then raising it won't make any
# difference, so first, let's lower it before establishing the baseline
@@ -368,9 +411,46 @@ class HotKeyTesting:
self.kb.press_key(KeyCodes.KEY_VOLUMEUP)
# let's grab output of alsa mixer to establish what is the baseline
# before we start raising the volume
+ vc = VolumeChange()
+ with self._monitored_volume_change(vc):
+ self.kb.press_key(KeyCodes.KEY_VOLUMEUP, repetitions=3, delay=0.2)
+ time.sleep(1)
+ return vc.before < vc.after
+
+ def check_volume_down(self):
+ """
+ Check if the volume down key has an effect on ALSA
+ """
+ # if the volume is already on min, then lowering it won't make any
+ # difference, so first, let's raise it before establishing the baseline
+ self.kb.press_key(KeyCodes.KEY_VOLUMEUP, repetitions=4, delay=0.2)
+ self.kb.press_key(KeyCodes.KEY_VOLUMEDOWN)
+ # let's grab output of alsa mixer to establish what is the baseline
+ # before we start raising the volume
+ vc = VolumeChange()
+ with self._monitored_volume_change(vc):
+ self.kb.press_key(
+ KeyCodes.KEY_VOLUMEDOWN, repetitions=3, delay=0.2)
+ return vc.before > vc.after
+
+ def check_mute(self):
+ """
+ Check if the mute key has an effect on ALSA
+ """
+ # first, let's raise volume (if it was already muted, then it will
+ # unmute it)
+ self.kb.press_key(KeyCodes.KEY_VOLUMEUP)
+ vc = VolumeChange()
+ with self._monitored_volume_change(vc):
+ self.kb.press_key(KeyCodes.KEY_MUTE)
+ time.sleep(1)
+ return vc.mute_after
+
+ @contextlib.contextmanager
+ def _monitored_volume_change(self, vc):
before = subprocess.check_output('amixer').decode(
sys.stdout.encoding).splitlines()
- self.kb.press_key(KeyCodes.KEY_VOLUMEUP, repetitions=3, delay=0.2)
+ yield
after = subprocess.check_output('amixer').decode(
sys.stdout.encoding).splitlines()
temp = before.copy()
@@ -382,21 +462,24 @@ class HotKeyTesting:
# all lines removed from before, so there's no state change
# of the stuff reported bevore hitting volume up, so let's fail
# the test
- return False
+ print('No change in amixer registered! ', end='')
+ return
# we expect that the lines that changed are status information about
# the output devices. Percentage volume is in square brackets so let's
# search for those and see if they got higher
- regex = re.compile(r'\[(\d*)%\]')
+ regex = re.compile(r'\[(\d*)%\].*\[(on|off)\]')
if len(before) != len(after):
# more of an assertion - the lines diff should match
- return False
+ return
for b, a in zip(before, after):
- vol_b = regex.search(b).groups()
- vol_a = regex.search(a).groups()
- if vol_a and vol_b:
- if int(vol_b[0]) < int(vol_a[0]):
- return True
- return False
+ match_b = regex.search(b)
+ match_a = regex.search(a)
+ if match_b and match_a:
+ vc.before = int(match_b.groups()[0])
+ vc.after = int(match_a.groups()[0])
+ vc.mute_before = match_b.groups()[1] == 'off'
+ vc.mute_after = match_a.groups()[1] == 'off'
+ return
def check_terminal_hotkey(self):
# spawn a terminal window using ctrl+alt+t
@@ -409,12 +492,48 @@ class HotKeyTesting:
time.sleep(2)
self.kb.type_text('touch {}'.format(filename))
self.kb.press_key(KeyCodes.KEY_ENTER)
- result = os.path.exists(filename)
- if not result:
- return result
+ for attempt_no in range(10):
+ # let's wait some time to let X/terminal process the command
+ time.sleep(0.5)
+ if os.path.exists(filename):
+ self.kb.press_key(KeyCodes.KEY_D, {'ctrl'})
+ os.unlink(filename)
+ return True
+ else:
+ self.kb.press_key(KeyCodes.KEY_D, {'ctrl'})
+ return False
+
+ def check_command_hotkey(self):
+ timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
+ filename = os.path.join('/tmp/hotkey-testing-cmd-{}'.format(timestamp))
+ self.kb.press_key(KeyCodes.KEY_F2, {'alt'})
+ assert(not os.path.exists(filename))
+ time.sleep(2)
+ self.kb.type_text('touch {}'.format(filename))
+ self.kb.press_key(KeyCodes.KEY_ENTER)
+ for attempt_no in range(10):
+ # let's wait some time to let X/terminal process the command
+ time.sleep(0.5)
+ if os.path.exists(filename):
+ os.unlink(filename)
+ return True
+ else:
+ return False
+
+ def check_media_play(self):
+ cmd = "timeout 30 rhythmbox --debug 2> /tmp/media-key-test"
+ self.kb.press_key(KeyCodes.KEY_T, {'ctrl', 'alt'})
+ time.sleep(2)
+ self.kb.type_text(cmd)
+ self.kb.press_key(KeyCodes.KEY_ENTER)
+ time.sleep(3)
+ self.kb.press_key(KeyCodes.KEY_PLAYPAUSE)
+ self.kb.press_key(KeyCodes.KEY_F4, {'alt'})
+ time.sleep(1)
self.kb.press_key(KeyCodes.KEY_D, {'ctrl'})
- os.unlink(filename)
- return result
+ with open('/tmp/media-key-test', 'rt') as f:
+ output = f.read()
+ return "got media key 'Play'" in output
def main():
diff --git a/bin/network_configs b/bin/network_configs
new file mode 100755
index 0000000..b2fcc99
--- /dev/null
+++ b/bin/network_configs
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+if `grep -q "replaced by netplan" /etc/network/interfaces`; then
+ # Get our configs from netplan
+ for directory in "etc" "run" "lib"; do
+ for configfile in `find /$directory/netplan -type f -name *.yaml`; do
+ echo "Config File $configfile:"
+ cat $configfile
+ echo ""
+ done
+ done
+else
+ # get configs from Network Manager instead
+ echo "Network Manager:"
+ nmcli device show
+fi
diff --git a/bin/network_device_info.py b/bin/network_device_info.py
index 29a4aed..4a827de 100755
--- a/bin/network_device_info.py
+++ b/bin/network_device_info.py
@@ -19,6 +19,11 @@
#
# Copyright (C) 2012-2019 Canonical, Ltd.
+import argparse
+import fcntl
+import os
+import socket
+import struct
from subprocess import check_output, CalledProcessError, STDOUT
import sys
@@ -28,234 +33,342 @@ from checkbox_support.parsers.modinfo import ModinfoParser
from checkbox_support.parsers.udevadm import UdevadmParser
-# This example lists basic information about network interfaces known to NM
-devtypes = {1: "Ethernet",
- 2: "WiFi",
- 5: "Bluetooth",
- 6: "OLPC",
- 7: "WiMAX",
- 8: "Modem"}
+class Utils():
-states = {0: "Unknown",
- 10: "Unmanaged",
- 20: "Unavailable",
- 30: "Disconnected",
- 40: "Prepare",
- 50: "Config",
- 60: "Need Auth",
- 70: "IP Config",
- 80: "IP Check",
- 90: "Secondaries",
- 100: "Activated",
- 110: "Deactivating",
- 120: "Failed"}
+ sys_path = '/sys/class/net'
-attributes = ("category", "interface", "product", "vendor", "driver", "path")
-
-udev_devices = []
-nm_devices = []
-
-
-class UdevResult:
- def addDevice(self, device):
- if device.category == 'NETWORK' and device.interface != "UNKNOWN":
- udev_devices.append(device)
-
-
-class NetworkingDevice():
- def __init__(self, devtype, props, dev_proxy, bus):
- self._devtype = devtype
+ @classmethod
+ def is_iface_connected(cls, iface):
try:
- self._interface = props['Interface']
- except KeyError:
- self._interface = "Unknown"
-
- try:
- self._ip = self._int_to_ip(props['Ip4Address'])
- except KeyError:
- self._ip = "Unknown"
-
- try:
- self._driver = props['Driver']
- except KeyError:
- self._driver = "Unknown"
- self._driver_ver = "Unknown"
-
- if self._driver != "Unknown":
- self._modinfo = self._modinfo_parser(props['Driver'])
- if self._modinfo:
- self._driver_ver = self._find_driver_ver()
- else:
- self._driver_ver = "Unknown"
-
- try:
- self._firmware_missing = props['FirmwareMissing']
- except KeyError:
- self._firmware_missing = False
-
+ carrier_file = os.path.join(cls.sys_path, iface, 'carrier')
+ return int(open(carrier_file, 'r').read()) == 1
+ except Exception:
+ pass
+ return False
+
+ @staticmethod
+ def get_ipv4_address(interface):
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ return socket.inet_ntoa(fcntl.ioctl(
+ s.fileno(),
+ 0x8915, # SIOCGIFADDR
+ struct.pack('256s', interface[:15].encode())
+ )[20:24])
+
+ @staticmethod
+ def get_ipv6_address(interface):
+ cmd = ['/sbin/ip', '-6', '-o', 'addr', 'show', 'dev', interface,
+ 'scope', 'link']
+ proc = check_output(cmd, universal_newlines=True)
+ return proc.split()[3].strip()
+
+ @classmethod
+ def get_mac_address(cls, interface):
+ address_file = os.path.join(cls.sys_path, interface, 'address')
try:
- self._state = states[props['State']]
- except KeyError:
- self._state = "Unknown"
-
- def __str__(self):
- ret = "Category: %s\n" % self._devtype
- ret += "Interface: %s\n" % self._interface
- ret += "IP: %s\n" % self._ip
- ret += "Driver: %s (ver: %s)\n" % (self._driver, self._driver_ver)
- if self._firmware_missing:
- ret += "Warning: Required Firmware Missing for device\n"
- ret += "State: %s\n" % self._state
- return ret
-
- def getstate(self):
- return self._state
-
- def gettype(self):
- return self._devtype
+ return open(address_file, 'r').read().strip()
+ except IOError:
+ return 'UNKNOWN'
- def _bitrate_to_mbps(self, bitrate):
+ @classmethod
+ def get_speed(cls, interface):
+ speed_file = os.path.join(cls.sys_path, interface, 'speed')
try:
- intbr = int(bitrate)
- return str(intbr / 1000)
- except Exception:
- return "NaN"
+ return open(speed_file, 'r').read().strip()
+ except IOError:
+ return 'UNKNOWN'
- def _modinfo_parser(self, driver):
+ @staticmethod
+ def get_driver_version(driver):
cmd = ['/sbin/modinfo', driver]
try:
- stream = check_output(cmd, stderr=STDOUT, universal_newlines=True)
+ output = check_output(cmd, stderr=STDOUT, universal_newlines=True)
except CalledProcessError:
return None
-
- if not stream:
+ if not output:
return None
- else:
- parser = ModinfoParser(stream)
- modinfo = parser.get_all()
+ parser = ModinfoParser(output)
+ modinfo = parser.get_all()
- return modinfo
-
- def _find_driver_ver(self):
# try the version field first, then vermagic second, some audio
# drivers don't report version if the driver is in-tree
- if self._modinfo['version'] and self._modinfo['version'] != 'in-tree:':
- return self._modinfo['version']
- else:
+ version = modinfo.get('version')
+
+ if version is None or version == 'in-tree':
# vermagic will look like this (below) and we only care about the
# first part:
# "3.2.0-29-generic SMP mod_unload modversions"
- return self._modinfo['vermagic'].split()[0]
-
- def _int_to_ip(self, int_ip):
- ip = [0, 0, 0, 0]
- ip[0] = int_ip & 0xff
- ip[1] = (int_ip >> 8) & 0xff
- ip[2] = (int_ip >> 16) & 0xff
- ip[3] = (int_ip >> 24) & 0xff
- return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3])
-
-
-def print_udev_devices():
- print("[ Devices found by udev ]".center(80, '-'))
- for device in udev_devices:
- for attribute in attributes:
- value = getattr(device, attribute)
- if value is not None:
- if attribute == 'driver':
- props = {}
- props['Driver'] = value
- network_dev = NetworkingDevice(
- None, props, None, None)
- print("%s: %s (ver: %s)" % (
- attribute.capitalize(), value,
- network_dev._driver_ver))
- else:
- print("%s: %s" % (attribute.capitalize(), value))
- vendor_id = getattr(device, 'vendor_id')
- product_id = getattr(device, 'product_id')
- subvendor_id = getattr(device, 'subvendor_id')
- subproduct_id = getattr(device, 'subproduct_id')
- if vendor_id and product_id:
- print("ID: [{0:04x}:{1:04x}]".format(
- vendor_id, product_id))
- if subvendor_id and subproduct_id:
- print("Subsystem ID: [{0:04x}:{1:04x}]".format(
- subvendor_id, subproduct_id))
- print()
-
-
-def get_nm_devices():
- devices = []
- bus = dbus.SystemBus()
-
- # Get a proxy for the base NetworkManager object
- proxy = bus.get_object("org.freedesktop.NetworkManager",
- "/org/freedesktop/NetworkManager")
- manager = dbus.Interface(proxy, "org.freedesktop.NetworkManager")
-
- # Get all devices known to NM and print their properties
- nm_devices = manager.GetDevices()
- for d in nm_devices:
- dev_proxy = bus.get_object("org.freedesktop.NetworkManager", d)
- prop_iface = dbus.Interface(dev_proxy,
- "org.freedesktop.DBus.Properties")
- props = prop_iface.GetAll("org.freedesktop.NetworkManager.Device")
- try:
- devtype = devtypes[props['DeviceType']]
- except KeyError:
- devtype = "Unknown"
-
- # only return Ethernet devices
- if devtype == "Ethernet":
- devices.append(NetworkingDevice(devtype, props, dev_proxy, bus))
- return devices
-
-
-def main():
- if len(sys.argv) != 2 or sys.argv[1] not in ('detect', 'info'):
- raise SystemExit('ERROR: please specify detect or info')
- action = sys.argv[1]
-
- try:
- output = check_output(['udevadm', 'info', '--export-db'])
- except CalledProcessError as err:
- raise SystemExit(err)
- try:
- output = output.decode("UTF-8", errors='ignore')
- except UnicodeDecodeError as err:
- raise SystemExit("udevadm output is not valid UTF-8")
- udev = UdevadmParser(output)
- result = UdevResult()
- udev.run(result)
-
- # The detect action should indicate presence of an ethernet adatper and
- # cause the job to fail if none present - rely on udev for this
- if action == 'detect':
- if udev_devices:
- print_udev_devices()
+ version = modinfo.get('vermagic').split()[0]
+
+ return version
+
+
+class NetworkDeviceInfo():
+
+ def __init__(self):
+ self._category = None
+ self._interface = None
+ self._product = None
+ self._vendor = None
+ self._driver = None
+ self._driver_version = None
+ self._firmware_missing = None
+ self._path = None
+ self._id = None
+ self._subsystem_id = None
+ self._mac = None
+ self._carrier_status = None
+ self._ipv4 = None
+ self._ipv6 = None
+ self._speed = None
+
+ def __str__(self):
+ ret = ""
+ for key, val in vars(self).items():
+ if val is not None:
+ # leading _ removed, remaining ones spaces
+ pretty_key = key.lstrip('_').replace('_', ' ').title()
+ ret += '{}: {}\n'.format(pretty_key, val)
+ return ret
+
+ @property
+ def interface(self):
+ return self._interface
+
+ @interface.setter
+ def interface(self, value):
+ self._interface = value
+ self._interface_populate()
+
+ @property
+ def driver(self):
+ return self._driver
+
+ @driver.setter
+ def driver(self, value):
+ self._driver = value
+ self._driver_populate()
+
+ @property
+ def category(self):
+ return self._category
+
+ @category.setter
+ def category(self, value):
+ self._category = value
+
+ @property
+ def product(self):
+ return self._product
+
+ @product.setter
+ def product(self, value):
+ self._product = value
+
+ @property
+ def vendor(self):
+ return self._vendor
+
+ @vendor.setter
+ def vendor(self, value):
+ self._vendor = value
+
+ @property
+ def firmware_missing(self):
+ return self._firmware_missing
+
+ @firmware_missing.setter
+ def firmware_missing(self, value):
+ self._firmware_missing = value
+
+ @property
+ def path(self):
+ return self._path
+
+ @path.setter
+ def path(self, value):
+ self._path = value
+
+ @property
+ def id(self):
+ return self._id
+
+ @id.setter
+ def id(self, value):
+ self._id = value
+
+ @property
+ def subsystem_id(self):
+ return self._subsystem_id
+
+ @subsystem_id.setter
+ def subsystem_id(self, value):
+ self._subsystem_id = value
+
+ def _interface_populate(self):
+ """Get extra attributes based on interface"""
+ if self.interface is None:
+ return
+ self._mac = Utils.get_mac_address(self.interface)
+ if Utils.is_iface_connected(self.interface):
+ self._carrier_status = 'Connected'
+ self._ipv4 = Utils.get_ipv4_address(self.interface)
+ self._ipv6 = Utils.get_ipv6_address(self.interface)
+ self._speed = Utils.get_speed(self.interface)
else:
- raise SystemExit('No devices detected by udev')
+ self._carrier_status = 'Disconnected'
+
+ def _driver_populate(self):
+ """Get extra attributes based on driver"""
+ if self.driver is None:
+ return
+ self._driver_version = Utils.get_driver_version(self.driver)
+
+
+class NMDevices():
+
+ # This example lists basic information about network interfaces known to NM
+ devtypes = {1: "Ethernet",
+ 2: "WiFi",
+ 5: "Bluetooth",
+ 6: "OLPC",
+ 7: "WiMAX",
+ 8: "Modem"}
+
+ def __init__(self, category="NETWORK"):
+ self.category = category
+ self._devices = []
+ self._collect_devices()
+
+ def __len__(self):
+ return len(self._devices)
+
+ def _collect_devices(self):
+ bus = dbus.SystemBus()
+ proxy = bus.get_object("org.freedesktop.NetworkManager",
+ "/org/freedesktop/NetworkManager")
+ manager = dbus.Interface(proxy, "org.freedesktop.NetworkManager")
+ self._devices = manager.GetDevices()
+
+ def devices(self):
+ """Convert to list of NetworkDevice with NM derived attrs set"""
+ for d in self._devices:
+ bus = dbus.SystemBus()
+ dev_proxy = bus.get_object("org.freedesktop.NetworkManager", d)
+ prop_iface = dbus.Interface(dev_proxy,
+ "org.freedesktop.DBus.Properties")
+ props = prop_iface.GetAll("org.freedesktop.NetworkManager.Device")
+ devtype = self.devtypes.get(props.get('DeviceType'))
+ if devtype is None:
+ continue
+ nd = NetworkDeviceInfo()
+ if self.category == "NETWORK":
+ if devtype == "Ethernet":
+ nd.category = self.category
+ else:
+ continue
+ if self.category == "WIRELESS":
+ if devtype == "WiFi":
+ nd.category = self.category
+ else:
+ continue
+ nd.interface = props.get('Interface')
+ nd.driver = props.get('Driver')
+ nd.firmware_missing = props.get('FirmwareMissing')
+ yield nd
- # The info action should just gather infomation about any ethernet devices
- # found and report for inclusion in e.g. an attachment job
- if action == 'info':
- # Report udev detected devices first
- if udev_devices:
- print_udev_devices()
- # Attempt to report devices found by NetworkManager - this doesn't
- # make sense for server installs so skipping is acceptable
+class UdevDevices():
+
+ def __init__(self, category='NETWORK'):
+ self.category = category
+ self._devices = []
+ self._collect_devices()
+
+ def __len__(self):
+ return len(self._devices)
+
+ def _collect_devices(self):
+ cmd = ['udevadm', 'info', '--export-db']
try:
- nm_devices = get_nm_devices()
- except dbus.exceptions.DBusException:
- # server's don't have network manager installed
- print('Network Manager not found')
- else:
- print("[ Devices found by Network Manager ]".center(80, '-'))
- for nm_dev in nm_devices:
- print(nm_dev)
+ output = check_output(cmd).decode(sys.stdout.encoding)
+ except CalledProcessError as err:
+ sys.stderr.write(err)
+ return
+ udev = UdevadmParser(output)
+ udev.run(self)
+
+ def addDevice(self, device):
+ """Callback for UdevadmParser"""
+ if device.category == self.category and device.interface != 'UNKNOWN':
+ self._devices.append(device)
+
+ def devices(self):
+ """Convert to list of NetworkDevice with UDev derived attrs set"""
+ for device in self._devices:
+ nd = NetworkDeviceInfo()
+ nd.category = getattr(device, 'category', None)
+ nd.interface = getattr(device, 'interface', None)
+ nd.product = getattr(device, 'product', None)
+ nd.vendor = getattr(device, 'vendor', None)
+ nd.driver = getattr(device, 'driver', None)
+ nd.path = getattr(device, 'path', None)
+ vid = getattr(device, 'vendor_id', None)
+ pid = getattr(device, 'product_id', None)
+ if vid and pid:
+ nd.id = '[{0:04x}:{1:04x}]'.format(vid, pid)
+ svid = getattr(device, 'subvendor_id', None)
+ spid = getattr(device, 'subproduct_id', None)
+ if svid and spid:
+ nd.subsystem_id = '[{0:04x}:{1:04x}]'.format(svid, spid)
+ yield nd
if __name__ == "__main__":
- main()
+ parser = argparse.ArgumentParser(
+ description='Gather information about network devices')
+ parser.add_argument('action', choices=('detect', 'info'),
+ help='Detect mode or just report info')
+ parser.add_argument('category', choices=('NETWORK', 'WIRELESS'),
+ help='Either ethernet or WLAN devices')
+ parser.add_argument('--no-nm', action='store_true',
+ help='Don\'t attempt to get info from network manager')
+ parser.add_argument('--interface',
+ help='Restrict info action to specified interface')
+ args = parser.parse_args()
+
+ udev = UdevDevices(args.category)
+
+ # The detect action should indicate presence of a device belonging to the
+ # category and cause the job to fail if none present
+ if args.action == 'detect':
+ if len(udev) == 0:
+ raise SystemExit('No devices detected by udev')
+ else:
+ print("[ Devices found by udev ]".center(80, '-'))
+ for device in udev.devices():
+ print(device)
+
+ # The info action should just gather infomation about any ethernet devices
+ # found and report for inclusion in e.g. an attachment job and include
+ # NetworkManager as a source if desired
+ if args.action == 'info':
+ # If interface has been specified
+ if args.interface:
+ for device in udev.devices():
+ if device.interface == args.interface:
+ print(device)
+ sys.exit(0)
+
+ # Report udev detected devices first
+ print("[ Devices found by udev ]".center(80, '-'))
+ for device in udev.devices():
+ print(device)
+
+ # Attempt to report devices found by NetworkManager. This can be
+ # skipped as doesn't make sense for server installs
+ if not args.no_nm:
+ nm = NMDevices(args.category)
+ print("[ Devices found by Network Manager ]".center(80, '-'))
+ for device in nm.devices():
+ print(device)
diff --git a/bin/network_info b/bin/network_info
deleted file mode 100755
index d3ff5df..0000000
--- a/bin/network_info
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import sys
-import subprocess
-import socket
-import fcntl
-import struct
-
-SYS_PATH = '/sys/class/net'
-
-
-def _read_file(file):
- source = open(file, 'r')
- content = source.read()
- source.close()
- return content
-
-
-def get_connected(interface):
- STATUS = ('No', 'Yes')
- carrier_file = os.path.join(SYS_PATH, interface, 'carrier')
-
- carrier = 0
- try:
- carrier = int(_read_file(carrier_file))
- except IOError:
- pass
-
- return STATUS[carrier]
-
-
-def get_ip_address(interface):
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- return socket.inet_ntoa(fcntl.ioctl(
- s.fileno(),
- 0x8915, # SIOCGIFADDR
- struct.pack('256s', interface[:15].encode())
- )[20:24])
-
-
-def get_ipv6_address(interface):
- cmd = ['/sbin/ip', '-6', '-o', 'addr', 'show',
- 'dev', interface, 'scope', 'link']
- proc = subprocess.check_output(cmd, universal_newlines=True)
- ipaddr = proc.split()[3].strip()
- return ipaddr
-
-
-def get_mac_address(interface):
- address_file = os.path.join(SYS_PATH, interface, 'address')
-
- address = ''
- try:
- address = _read_file(address_file).strip()
- except IOError:
- pass
-
- return address
-
-
-def get_speed(interface):
- speed_file = os.path.join(SYS_PATH, interface, 'speed')
-
- speed = ''
- try:
- speed = _read_file(speed_file).strip()
- except IOError:
- pass
-
- return speed
-
-
-def main(args):
- for interface in args:
- connected = get_connected(interface)
- print("Interface: %s" % interface)
- print("Connected: %s" % connected)
- try:
- print("IPv4: %s" % get_ip_address(interface))
- except IOError:
- print("IPv4: n/a")
- try:
- print("IPv6 (link local): %s" % get_ipv6_address(interface))
- except IOError:
- print("IPv6 (link local): n/a")
- except:
- print("IPv6 (link local): n/a")
- print("MAC: %s" % get_mac_address(interface))
- print("Connect Speed: %s\n" % get_speed(interface))
-
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(main(sys.argv[1:]))
diff --git a/bin/pm_test b/bin/pm_test
index dd9e1a0..f455858 100755
--- a/bin/pm_test
+++ b/bin/pm_test
@@ -188,7 +188,7 @@ class PowerManagementOperation():
command_tpl = '-s s3 --s3-device-check ' \
'--s3-sleep-delay=30 --s3-multiple={}'
if self.args.log_dir:
- command_tpl = '--log={}/fwts.log '.format(
+ command_tpl = '--log={}/fwts '.format(
self.args.log_dir) + command_tpl
command_tpl = '{} ' + command_tpl
else:
diff --git a/bin/rotation_test b/bin/rotation_test
index 258a881..6bf90fd 100755
--- a/bin/rotation_test
+++ b/bin/rotation_test
@@ -5,14 +5,14 @@
#
# This file is part of Checkbox.
#
-# Copyright 2012 Canonical Ltd.
+# Copyright 2012-2019 Canonical Ltd.
#
# Authors: Alberto Milone <alberto.milone@canonical.com>
+# Maciej Kisielewski <maciej.kisielewski@canonical.com>
#
# 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
@@ -22,53 +22,24 @@
# You should have received a copy of the GNU General Public License
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
+import gi
import time
-import sys
-
-from checkbox_support.contrib import xrandr
-
+import subprocess
-def rotate_screen(rotation):
- # Refresh the screen. Required by NVIDIA
- screen = xrandr.get_current_screen()
- screen.set_rotation(rotation)
- return screen.apply_config()
+gi.require_version('Gdk', '3.0')
+from gi.repository import Gdk
def main():
- screen = xrandr.get_current_screen()
- rotations = {'normal': xrandr.RR_ROTATE_0,
- 'right': xrandr.RR_ROTATE_90,
- 'inverted': xrandr.RR_ROTATE_180,
- 'left': xrandr.RR_ROTATE_270}
- rots_statuses = {}
-
- for rot in rotations:
- try:
- status = rotate_screen(rotations[rot])
- except (xrandr.RRError, xrandr.UnsupportedRRError) as exc:
- status = 1
- error = exc
- else:
- error = 'N/A'
- # Collect the status and the error message
- rots_statuses[rot] = (status, error)
+ """Run rotation cycling by running xrandr command."""
+ screen = Gdk.Screen.get_default()
+ output = screen.get_monitor_plug_name(screen.get_primary_monitor())
+ print("Using output: {}".format(output))
+
+ for rotation in ['right', 'inverted', 'left', 'normal']:
+ print("Changing rotation to: {}".format(rotation))
+ subprocess.check_call(
+ ['xrandr', '--output', output, '--rotation', rotation])
time.sleep(4)
- # Try to set the screen back to normal
- try:
- rotate_screen(xrandr.RR_ROTATE_0)
- except(xrandr.RRError, xrandr.UnsupportedRRError) as error:
- print(error)
-
- result = 0
- for elem in rots_statuses:
- status = rots_statuses.get(elem)[0]
- error = rots_statuses.get(elem)[1]
- if status != 0:
- print('Error: rotation "%s" failed with status %d: %s.' %
- (elem, status, error), file=sys.stderr)
- result = 1
- return result
-
if __name__ == '__main__':
exit(main())
diff --git a/bin/storage_test b/bin/storage_test
deleted file mode 100755
index 9649dc1..0000000
--- a/bin/storage_test
+++ /dev/null
@@ -1,213 +0,0 @@
-#!/bin/bash
-
-# take the path of the storage device and test is it a block device.
-
-function run_bonnie() {
- echo "Running bonnie++ on $1..."
- mount_point=$(df -h | grep -m 1 $1 | awk '{print $6}')
- echo "Putting scratch disk at $mount_point"
- mkdir -p "$mount_point/tmp/scratchdir"
-
- # When running on disks with small drives (SSD/flash) we need to do
- # some tweaking. Bonnie uses 2x RAM by default to write data. If that's
- # more than available disk space, the test will fail inappropriately.
- free_space=$(df -m | grep -m 1 $1 | awk '{print $4}')
- echo " Disk $1 has ${free_space}MB available"
- memory=$(free -m |grep Mem|awk '{print $2}')
- echo " System has ${memory}MB RAM"
- disk_needed=$((memory * 2))
- echo " We need ${disk_needed}MB of disk space for testing"
- if [[ "$disk_needed" -ge "$free_space" ]]; then
- echo " Insufficient disk space available for defaults."
- echo " reducing memory footprint to be 1/2 of free disk space."
- # we need to pass an amount that's 1/2 the amount of available disk
- # space to bonnie++ so she doesn't overwrite and fail.
- disk_needed=$(($free_space/2))
- bonnie++ -d $mount_point/tmp/scratchdir -u root -r $disk_needed
- else
- echo " Free disk space is sufficient to continue testing."
- bonnie++ -d $mount_point/tmp/scratchdir -u root
- fi
-}
-
-# Find the largest logical volume in an LVM partition.
-# Output:
-# $largest_part -- Device filename of largest qualifying partition
-# $largest_size -- Size of largest qualifying partition
-# $largest_fs -- Filesystem (ext4, etc.) used on largest qualifying partition
-# Note: Above variables are initialized in find_largest_partition(), which
-# calls this function.
-# Caveat: If LVM is used, there can be no guarantee that a specific disk
-# device is actually being tested. Thus, an LVM configuration should span
-# just one disk device. LVM may be used on one disk, but subsequent disks
-# should use "raw" partitions.
-find_largest_lv() {
- local partonly=$(echo $partition | cut -f 3 -d "/")
- for syslv in $(ls -d /sys/block/dm-*/slaves/$partonly) ; do
- lv=$(echo "$syslv" | cut -f 4 -d "/")
- size=$(cat /sys/block/$lv/size)
- sector_size=$(cat /sys/block/$lv/queue/hw_sector_size)
- let size=$size*$sector_size
- local blkid_info=$(blkid -s TYPE /dev/$lv | grep -E ext2\|ext3\|ext4\|xfs\|jfs\|btrfs)
- if [ "$size" -gt "$largest_size" ] && [ -n "$blkid_info" ] ; then
- local blkid_info=$(blkid -s TYPE /dev/$lv)
- largest_size=$size
- largest_part="/dev/$lv"
- largest_fs=$(blkid -s TYPE "/dev/$lv" | cut -d "=" -f 2)
- fi
- done
-} # find_largest_lv()
-
-
-# Find the largest partition that holds a supported filesystem on $disk.
-# This code is adapted from a similar function in `disk_stress_ng`.
-# Output:
-# $largest_part -- Device filename of largest qualifying partition or logical volume
-# $largest_size -- Size of largest qualifying partition or logical volume
-# $largest_fs -- Filesystem (ext4, etc.) used on largest qualifying partition or logical volume
-# $unsupported_fs -- Empty or contains name of unsupported filesystem found on disk
-find_largest_partition() {
- echo "Find largest partition on $disk..."
- largest_part=""
- largest_size=0
- mapper_string="dm-"
- if [ "${disk_device#*$mapper_string}" = "$disk" ]; then
- partitions=$(lsblk -b -l -n -o NAME,SIZE,TYPE,MOUNTPOINT $disk | grep part | tr -s " ")
- else
- partitions=$(lsblk -b -l -n -o NAME,SIZE,TYPE,MOUNTPOINT $disk)
- fi
- unsupported_fs=""
- for partition in $(echo "$partitions" | cut -d " " -f 1) ; do
- part_size=$(echo "$partitions" | grep "$partition " | cut -d " " -f 2)
- if [ -b "/dev/$partition" ]; then
- part_location="/dev/$partition"
- elif [ -b "/dev/mapper/$partition" ]; then
- part_location="/dev/mapper/$partition"
- else
- echo "$partition not found!"
- echo "Aborting test"
- exit 1
- fi
- local blkid_info=$(blkid -s TYPE $part_location | grep -E ext2\|ext3\|ext4\|xfs\|jfs\|btrfs\|LVM2_member)
- if [ "$part_size" > "$largest_size" ] && [ -n "$blkid_info" ] ; then
- if [[ "$blkid_info" =~ .*LVM2_member.* ]] ; then
- find_largest_lv
- else
- largest_size=$part_size
- largest_part="$part_location"
- largest_fs=$(blkid -s TYPE "$part_location" | cut -d "=" -f 2)
- fi
- fi
- local blkid_info=$(blkid -s TYPE $part_location | grep -E ntfs\|vfat\|hfs)
- if [ -n "$blkid_info" ] ; then
- # If there's an NTFS, HFS+, or FAT filesystem on the disk make note of it....
- unsupported_fs=$(blkid -s TYPE "/dev/$partition" | cut -d "=" -f 2)
- fi
- done
-} # find_largest_partition()
-
-
-if [[ "$1}" =~ dm-.* ]]; then
- # assume lvm or multipath and look for the dev/mapper device
- disk=/dev/mapper/`ls -l /dev/mapper/ | grep $1 | awk '{print $9}'`
-else
- # if the disk doesn't look like dm-0
- disk=/dev/$1
-fi
-echo "Set disk to $disk"
-scripted_mount=0
-
-if [ -b "$disk" ]
-then
- echo "$disk is a block device"
-
- #Add a check for warnings
- WARN=$(parted -s ${disk} print | grep "^Warning.*${disk}.*[Rr]ead-only" 2>&1)
- if [[ $? == 0 ]]
- then
- echo "Warning found in parted output:"
- echo $WARN
- echo "Aborting Test"
- exit 1
- fi
-
- # Regex changed to better handle when $disk appears more than once
- # in parted output (such as in warning messages or not caught in the
- # check above)
- size=`parted -l -s 2>&1 | grep "Disk .*${disk}:" | awk '{print $3}'`
-
- if [ -n "$size" ]
- then
- echo "$disk reports a size of $size."
- # Have to account for the end of the size descriptor
- size_range=${size:(-2)}
-
- if mount | grep -q $disk
- then
- echo "$disk is mounted, proceeding."
- else
- echo "$disk is not mounted. It must be mounted before testing."
- find_largest_partition
- if [ -n "$largest_part" ]
- then
- dest=`mktemp -dq --tmpdir drive.XXX`
- echo "Mounting $largest_part into $dest..."
- mount $largest_part $dest
- if [[ $? == 0 ]]
- then
- scripted_mount=1
- fi
- else
- echo "$disk has no partition. Please format this drive and re-launch the test."
- exit 1
- fi
- fi
-
-
- if [ "$size_range" == "KB" ]
- then
- echo "$disk size reported in KB, seems to be too small for testing."
- exit 1
- elif [ "$size_range" == "MB" ]
- then
- size_int=${size::${#size}-2}
-
- if [ "$size_int" -gt 10 ]
- then
- run_bonnie $disk
- if [[ $scripted_mount == 1 ]]
- then
- echo "$largest_part was mounted by this script. Unmounting it now..."
- umount $largest_part
- echo "Removing temporary mount directory $dest..."
- rm -rf $dest
- fi
- else
- echo "$disk is too small to be used for testing."
- if [[ $scripted_mount == 1 ]]
- then
- echo "$largest_part was mounted by this script. Unmounting it now..."
- umount $largest_part
- echo "Removing temporary mount directory $dest..."
- rm -rf $dest
- fi
- exit 1
- fi
- else
- run_bonnie $disk
- if [[ $scripted_mount == 1 ]]
- then
- echo "$largest_part was mounted by this script. Unmounting it now..."
- umount $largest_part
- echo "Removing temporary mount directory $dest..."
- rm -rf $dest
- fi
- fi
- else
- echo "$disk doesn't report a size."
- exit 1
- fi
-else
- echo "$disk is not listed as a block device."
- exit 1
-fi
diff --git a/bin/storage_test.py b/bin/storage_test.py
new file mode 100755
index 0000000..3dbc87f
--- /dev/null
+++ b/bin/storage_test.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+# Copyright 2019 Canonical Ltd.
+# All rights reserved.
+#
+# Written by:
+# Jonathan Cave <jonathan.cave@canonical.com>
+#
+# Perfrom bonnie++ disk test
+
+from collections import namedtuple
+from contextlib import ExitStack
+import os
+import subprocess as sp
+import sys
+import tempfile
+
+import psutil
+
+
+def mountpoint(device):
+ for part in psutil.disk_partitions():
+ if part.device == device:
+ return part.mountpoint
+ return None
+
+
+def find_largest_partition(device):
+ BlkDev = namedtuple('BlkDev', ['name', 'size', 'type'])
+ cmd = 'lsblk -b -l -n -o NAME,SIZE,TYPE {}'.format(device)
+ out = sp.check_output(cmd, shell=True)
+ 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']
+ blk_devs.sort(key=lambda bd: int(bd.size))
+ return blk_devs[-1].name
+
+
+def mount(source, target):
+ cmd = 'mount {} {}'.format(source, target)
+ print('+', cmd, flush=True)
+ sp.check_call(cmd, shell=True)
+
+
+def unmount(target):
+ cmd = 'unmount {}'.format(target)
+ print('+', cmd, flush=True)
+ sp.check_call(cmd, shell=True)
+
+
+def memory():
+ return psutil.virtual_memory().total / (1024 * 1024)
+
+
+def free_space(test_dir):
+ du = psutil.disk_usage(test_dir)
+ return du.free / (1024 * 1024)
+
+
+def devmapper_name(udev_name):
+ dm_name = None
+ sys_d = '/sys/block/{}'.format(udev_name)
+ if os.path.isdir(os.path.join(sys_d, 'dm')):
+ with open('/sys/block/{}/dm/name'.format(udev_name), 'r') as f:
+ dm_name = f.read().strip()
+ return dm_name
+
+
+def run_bonnie(test_dir, user='root'):
+ # Set a maximum size on the amount of RAM, this has the effect of keeping
+ # the amount of data written during tests lower than default. This keeps
+ # duration of tests at something reasonable
+ force_mem_mb = 8000
+ if memory() < force_mem_mb:
+ force_mem_mb = memory()
+ # When running on disks with small drives (SSD/flash) we need to do
+ # some tweaking. Bonnie uses 2x RAM by default to write data. If that's
+ # more than available disk space, the test will fail inappropriately.
+ free = free_space(test_dir)
+ print('{}MB of free space avaialble'.format(free))
+ if (force_mem_mb * 2) > free:
+ force_mem_mb = free / 2
+ 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)
+ sp.check_call(cmd, shell=True)
+
+
+def devmapper_test(udev_name):
+ print('identified as a devmapper device...')
+ device = '/dev/{}'.format(udev_name)
+ mount_dir = mountpoint(device)
+ if mount_dir:
+ print('{} already mounted at {}'.format(device, mount_dir))
+ else:
+ dm_name = devmapper_name(udev_name)
+ if dm_name:
+ dm_device = os.path.join('/dev/mapper', dm_name)
+ if os.path.exists(dm_device):
+ mount_dir = mountpoint(dm_device)
+ if mount_dir:
+ print('{} already mounted at {}'.format(
+ dm_device, mount_dir))
+ with ExitStack() as stack:
+ if mount_dir is None:
+ mount_dir = tempfile.mkdtemp()
+ stack.callback(os.rmdir, mount_dir)
+ mount(device, mount_dir)
+ print('Performed mount of {} at {}'.format(device, mount_dir))
+ stack.callback(unmount, mount_dir)
+ run_bonnie(mount_dir)
+
+
+def disk_test(udev_name):
+ print('identified as a disk...')
+ device = '/dev/{}'.format(udev_name)
+ part_to_test = '/dev/{}'.format(find_largest_partition(device))
+ print('test will be run on partition {}'.format(part_to_test))
+ mount_dir = mountpoint(part_to_test)
+ if mount_dir:
+ print('{} already mounted at {}'.format(part_to_test, mount_dir))
+ with ExitStack() as stack:
+ if mount_dir is None:
+ mount_dir = tempfile.mkdtemp()
+ stack.callback(os.rmdir, mount_dir)
+ mount(part_to_test, mount_dir)
+ print('Performed mount {} at {}'.format(part_to_test, mount_dir))
+ stack.callback(unmount, mount_dir)
+ run_bonnie(mount_dir)
+
+
+def main():
+ udev_name = sys.argv[1]
+ print('Testing disk {}'.format(udev_name))
+
+ # handle dev mapper and regular disks seperately
+ if devmapper_name(udev_name):
+ devmapper_test(udev_name)
+ else:
+ disk_test(udev_name)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/bin/wifi_client_test_netplan.py b/bin/wifi_client_test_netplan.py
new file mode 100755
index 0000000..c74320e
--- /dev/null
+++ b/bin/wifi_client_test_netplan.py
@@ -0,0 +1,321 @@
+#!/usr/bin/env python3
+# Copyright 2018-2019 Canonical Ltd.
+# All rights reserved.
+#
+# Written by:
+# Gavin Lin <gavin.lin@canonical.com>
+# Jonathan Cave <jonathan.cave@canonical.com>
+
+"""
+This script will test Wi-Fi device with netplan automatically.
+
+To see how to use, please run "./wifi_client_test_netplan.py --help"
+"""
+
+import argparse
+import datetime
+import functools
+import glob
+import os
+import subprocess as sp
+import textwrap
+import time
+import shutil
+from struct import pack
+from socket import inet_ntoa
+import sys
+
+print = functools.partial(print, flush=True)
+
+
+def print_head(txt):
+ print("##", txt)
+
+
+def print_cmd(cmd):
+ print("+", cmd)
+
+
+# Configuration file path
+if "SNAP_USER_DATA" in os.environ:
+ TMP_PATH = os.path.join(os.environ["SNAP_USER_DATA"],
+ "WIFI-TEST-NETPLAN-CREATED-BY-CHECKBOX")
+else:
+ TMP_PATH = os.path.join("/tmp", "WIFI-TEST-NETPLAN-CREATED-BY-CHECKBOX")
+
+NETPLAN_CFG_PATHS = ("/etc/netplan", "/lib/netplan", "/run/netplan")
+NETPLAN_TEST_CFG = "/etc/netplan/99-CREATED-BY-CHECKBOX.yaml"
+
+
+def netplan_config_backup():
+ print_head("Backup any existing netplan configuration files")
+ if os.path.exists(TMP_PATH):
+ print("Clear backup location")
+ shutil.rmtree(TMP_PATH)
+ for basedir in NETPLAN_CFG_PATHS:
+ print("Checking in {}".format(basedir))
+ files = glob.glob(os.path.join(basedir, '*.yaml'))
+ if files:
+ backup_loc = os.path.join(TMP_PATH, *basedir.split('/'))
+ os.makedirs(backup_loc)
+ for f in files:
+ print(" ", f)
+ shutil.copy(f, backup_loc)
+ print()
+
+
+def netplan_config_wipe():
+ print_head("Delete any existing netplan configuration files")
+ # NOTE: this removes not just configs for wifis, but for all device types
+ # (ethernets, bridges) which could be dangerous
+ for basedir in NETPLAN_CFG_PATHS:
+ print("Wiping {}".format(basedir))
+ files = glob.glob(os.path.join(basedir, '*.yaml'))
+ for f in files:
+ print(" ", f)
+ os.remove(f)
+
+ # If there's any file left in configuration folder then there's something
+ # not expected, stop the test
+ for basedir in NETPLAN_CFG_PATHS:
+ files = glob.glob(os.path.join(basedir, "*.yaml"))
+ if files:
+ print("ERROR: Failed to wipe netplan config files:")
+ for f in files:
+ print(" ", f)
+ netplan_config_restore()
+ raise SystemExit("Configuration file restored, exiting...")
+ print()
+
+
+def netplan_config_restore():
+ print_head("Restore configuration files")
+ files = glob.glob("{}/**/*.yaml".format(TMP_PATH), recursive=True)
+ if files:
+ print("Restoring:")
+ for f in files:
+ restore_loc = f[len(TMP_PATH):]
+ print(" ", restore_loc)
+ try:
+ shutil.move(f, restore_loc)
+ except shutil.Error:
+ raise SystemExit("Failed to restore {}".format(f))
+
+
+def generate_test_config(interface, ssid, psk, address, dhcp):
+ """
+ Produce valid netplan yaml from arguments provided
+
+ Typical open ap with dhcp:
+
+ >>> print(generate_test_config("eth0", "my_ap", None, "", True))
+ # This is the network config written by checkbox
+ network:
+ version: 2
+ wifis:
+ eth0:
+ access-points:
+ my_ap: {}
+ addresses: []
+ dhcp4: True
+ nameservers: {}
+
+ Typical private ap with dhcp:
+
+ >>> print(generate_test_config("eth0", "my_ap", "s3cr3t", "", True))
+ # This is the network config written by checkbox
+ network:
+ version: 2
+ wifis:
+ eth0:
+ access-points:
+ my_ap: {password: s3cr3t}
+ addresses: []
+ dhcp4: True
+ nameservers: {}
+
+ Static IP no dhcp:
+ >>> print(generate_test_config("eth0", "my_ap", "s3cr3t", "192.168.1.1", False))
+ # This is the network config written by checkbox
+ network:
+ version: 2
+ wifis:
+ eth0:
+ access-points:
+ my_ap: {password: s3cr3t}
+ addresses: [192.168.1.1]
+ dhcp4: False
+ nameservers: {}
+ """
+ np_cfg = """\
+ # This is the network config written by checkbox
+ network:
+ version: 2
+ wifis:
+ {0}:
+ access-points:
+ {1}: {{{2}}}
+ addresses: [{3}]
+ dhcp4: {4}
+ nameservers: {{}}"""
+ if psk:
+ password = "password: " + psk
+ else:
+ password = ""
+ return textwrap.dedent(np_cfg.format(interface, ssid, password, address, dhcp))
+
+
+def write_test_config(config):
+ print_head("Write the test netplan config file to disk")
+ with open(NETPLAN_TEST_CFG, "w", encoding="utf-8") as f:
+ f.write(config)
+ print()
+
+
+def delete_test_config():
+ print_head("Delete the test file")
+ os.remove(NETPLAN_TEST_CFG)
+ print()
+
+
+def netplan_apply_config():
+ cmd = "netplan --debug apply"
+ print_cmd(cmd)
+ # Make sure the python env used by netplan is from the base snap
+ env = os.environ
+ env.pop('PYTHONHOME', None)
+ env.pop('PYTHONPATH', None)
+ env.pop('PYTHONUSERBASE', None)
+ retcode = sp.call(cmd, shell=True, env=env)
+ if retcode != 0:
+ print("ERROR: failed netplan apply call")
+ print()
+ return False
+ print()
+ return True
+
+
+def _get_networkctl_state(interface):
+ cmd = 'networkctl status --no-pager --no-legend {}'.format(interface)
+ output = sp.check_output(cmd, shell=True)
+ for line in output.decode(sys.stdout.encoding).splitlines():
+ key, val = line.strip().split(':', maxsplit=1)
+ if key == "State":
+ return val
+
+
+def wait_for_routable(interface, max_wait=15):
+ routable = False
+ attempts = 0
+ while not routable and attempts < max_wait:
+ state = _get_networkctl_state(interface)
+ print(state)
+ if "routable" in state:
+ routable = True
+ break
+ time.sleep(1)
+ attempts += 1
+ if routable:
+ print("Reached routable state")
+ else:
+ print("WARN: did not reach routable state")
+ print()
+
+
+def perform_ping_test(interface):
+ cmd = 'gateway_ping_test -v --interface={}'.format(interface)
+ print_cmd(cmd)
+ retcode = sp.call(cmd, shell=True)
+ return retcode == 0
+
+
+def print_journal_entries(start):
+ print_head("Journal Entries")
+ cmd = ('journalctl -q --no-pager '
+ '-u systemd-networkd.service '
+ '-u wpa_supplicant.service '
+ ' -u netplan-* '
+ '--since "{}" '.format(start.strftime('%Y-%m-%d %H:%M:%S')))
+ print_cmd(cmd)
+ sp.call(cmd, shell=True)
+
+
+def main():
+ # Read arguments
+ parser = argparse.ArgumentParser(
+ description=("This script will test wireless network with netplan"
+ " in client mode."))
+ parser.add_argument(
+ "-i", "--interface", type=str, help=(
+ "The interface which will be tested, default is wlan0"),
+ default="wlan0")
+ parser.add_argument(
+ "-s", "--ssid", type=str, help=(
+ "SSID of target network, this is required argument"),
+ required=True)
+ parser.add_argument(
+ "-k", "--psk", type=str, help=(
+ "Pre-shared key of target network, this is optional argument,"
+ " only for PSK protected network"))
+ ip_method = parser.add_mutually_exclusive_group(required=True)
+ ip_method.add_argument(
+ "-d", "--dhcp", action='store_true', help=(
+ "Enable DHCP for IPv4"),
+ default=False)
+ ip_method.add_argument(
+ "-a", "--address", type=str, help=(
+ "Set ip address and netmask for the test interface,"
+ " example: 192.168.1.1/24"),
+ default="")
+ args = parser.parse_args()
+
+ start_time = datetime.datetime.now()
+
+ netplan_config_backup()
+ netplan_config_wipe()
+
+ # Create wireless network test configuration file
+ print_head("Generate a test netplan configuration")
+ config_data = generate_test_config(**vars(args))
+ print(config_data)
+ print()
+
+ write_test_config(config_data)
+
+ # Bring up the interface
+ print_head("Apply the test configuration")
+ if not netplan_apply_config():
+ delete_test_config()
+ netplan_config_restore()
+ print_journal_entries(start_time)
+ raise SystemExit(1)
+ time.sleep(20)
+
+ print_head("Wait for interface to be routable")
+ wait_for_routable(args.interface)
+
+ # Check connection by ping or link status
+ print_head("Perform a ping test")
+ test_result = perform_ping_test(args.interface)
+ if test_result:
+ print("Connection test passed\n")
+ else:
+ print("Connection test failed\n")
+
+ delete_test_config()
+ netplan_config_restore()
+
+ if not netplan_apply_config():
+ print_journal_entries(start_time)
+ raise SystemExit("ERROR: failed to apply restored config")
+
+ if not test_result:
+ print_journal_entries(start_time)
+ raise SystemExit(1)
+
+
+if __name__ == "__main__":
+ # Check if executed with root
+ if os.geteuid() != 0:
+ raise SystemExit("Error: please run this command as root")
+ main()
diff --git a/bin/wifi_nmcli_test.py b/bin/wifi_nmcli_test.py
index 971b363..5e5cb6e 100755
--- a/bin/wifi_nmcli_test.py
+++ b/bin/wifi_nmcli_test.py
@@ -10,13 +10,13 @@
import argparse
+import datetime
import functools
+import os
import subprocess as sp
import sys
import time
-from distutils.version import LooseVersion
-
print = functools.partial(print, flush=True)
@@ -29,17 +29,6 @@ def print_cmd(cmd):
print("+", cmd)
-def legacy_nmcli():
- cmd = "nmcli -v"
- output = sp.check_output(cmd, shell=True)
- version = LooseVersion(output.strip().split()[-1].decode())
- # check if using an earlier nmcli version with different api
- # nmcli in trusty is 0.9.8.8
- if version < LooseVersion("0.9.9"):
- return True
- return False
-
-
def cleanup_nm_connections():
print_head("Cleaning up NM connections")
cmd = "nmcli -t -f TYPE,UUID,NAME c"
@@ -49,10 +38,7 @@ def cleanup_nm_connections():
type, uuid, name = line.strip().split(':')
if type == '802-11-wireless':
print("Deleting connection", name)
- if legacy_nmcli():
- cmd = "nmcli c delete uuid {}".format(uuid)
- else:
- cmd = "nmcli c delete {}".format(uuid)
+ cmd = "nmcli c delete {}".format(uuid)
print_cmd(cmd)
sp.call(cmd, shell=True)
print()
@@ -75,27 +61,18 @@ def device_rescan():
def list_aps(args):
print_head("List APs")
count = 0
- if legacy_nmcli():
- fields = "SSID,FREQ,SIGNAL"
- cmd = "nmcli -t -f {} d wifi list iface {}".format(fields, args.device)
- else:
- fields = "SSID,CHAN,FREQ,SIGNAL"
- cmd = "nmcli -t -f {} d wifi list ifname {}".format(
- fields, args.device)
+ fields = "SSID,CHAN,FREQ,SIGNAL"
+ cmd = "nmcli -t -f {} d wifi list ifname {}".format(
+ fields, args.device)
print_cmd(cmd)
output = sp.check_output(cmd, shell=True)
for line in output.decode(sys.stdout.encoding).splitlines():
# lp bug #1723372 - extra line in output on zesty
if line.strip() == args.device:
continue
- if legacy_nmcli():
- ssid, frequency, signal = line.strip().split(':')
- print("SSID: {} Freq: {} Signal: {}".format(
- ssid, frequency, signal))
- else:
- ssid, channel, frequency, signal = line.strip().split(':')
- print("SSID: {} Chan: {} Freq: {} Signal: {}".format(
- ssid, channel, frequency, signal))
+ ssid, channel, frequency, signal = line.strip().split(':')
+ print("SSID: {} Chan: {} Freq: {} Signal: {}".format(
+ ssid, channel, frequency, signal))
if hasattr(args, 'essid'):
if ssid == args.essid:
count += 1
@@ -107,20 +84,12 @@ def list_aps(args):
def open_connection(args):
print_head("Connection attempt")
- if legacy_nmcli():
- cmd = "nmcli d wifi connect {} iface {} name TEST_CON".format(
- args.essid, args.device)
- else:
- cmd = "nmcli d wifi connect {} ifname {} name TEST_CON".format(
- args.essid, args.device)
+ cmd = "nmcli d wifi connect {} ifname {} name TEST_CON".format(
+ args.essid, args.device)
print_cmd(cmd)
sp.call(cmd, shell=True)
- if legacy_nmcli():
- cmd = ("nmcli -m tabular -t -f GENERAL d list | grep {} | "
- "awk -F: '{{print $15}}'".format(args.device))
- else:
- cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format(
- args.device)
+ cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format(
+ args.device)
print_cmd(cmd)
output = sp.check_output(cmd, shell=True)
state = output.decode(sys.stdout.encoding).strip()
@@ -134,20 +103,12 @@ def open_connection(args):
def secured_connection(args):
print_head("Connection attempt")
- if legacy_nmcli():
- cmd = ("nmcli d wifi connect {} password {} iface {} name "
- "TEST_CON".format(args.essid, args.psk, args.device))
- else:
- cmd = ("nmcli d wifi connect {} password {} ifname {} name "
- "TEST_CON".format(args.essid, args.psk, args.device))
+ cmd = ("nmcli --wait 180 d wifi connect {} password {} ifname {} name "
+ "TEST_CON".format(args.essid, args.psk, args.device))
print_cmd(cmd)
sp.call(cmd, shell=True)
- if legacy_nmcli():
- cmd = ("nmcli -m tabular -t -f GENERAL d list | "
- "grep {} | awk -F: '{{print $15}}'".format(args.device))
- else:
- cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format(
- args.device)
+ cmd = "nmcli -m tabular -t -f GENERAL.STATE d show {}".format(
+ args.device)
print_cmd(cmd)
output = sp.check_output(cmd, shell=True)
state = output.decode(sys.stdout.encoding).strip()
@@ -196,6 +157,17 @@ def hotspot(args):
return retcode
+def print_journal_entries(start):
+ print_head("Journal Entries")
+ cmd = ('journalctl -q --no-pager '
+ '-u snap.network-manager.networkmanager.service '
+ '-u NetworkManager.service '
+ '-u wpa_supplicant.service '
+ '--since "{}" '.format(start.strftime('%Y-%m-%d %H:%M:%S')))
+ print_cmd(cmd)
+ sp.call(cmd, shell=True)
+
+
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='WiFi connection test using mmcli')
@@ -232,9 +204,10 @@ if __name__ == '__main__':
args = parser.parse_args()
+ start_time = datetime.datetime.now()
+
cleanup_nm_connections()
- if not legacy_nmcli():
- device_rescan()
+ device_rescan()
count = list_aps(args)
if args.test_type == 'scan':
@@ -247,6 +220,13 @@ if __name__ == '__main__':
if args.func:
try:
- sys.exit(args.func(args))
- finally:
+ result = args.func(args)
+ except:
cleanup_nm_connections()
+
+ # The test is not required to run as root, but root access is required for
+ # journal access so only attempt to print when e.g. running under Remote
+ if result != 0 and os.geteuid() == 0:
+ print_journal_entries(start_time)
+
+ sys.exit(result)
diff --git a/data/edids/1280x1024.edid b/data/edids/1280x1024.edid
new file mode 100644
index 0000000..057bfec
--- /dev/null
+++ b/data/edids/1280x1024.edid
Binary files differ
diff --git a/data/edids/1920x1080.edid b/data/edids/1920x1080.edid
new file mode 100644
index 0000000..567437d
--- /dev/null
+++ b/data/edids/1920x1080.edid
Binary files differ
diff --git a/data/edids/2560x1440.edid b/data/edids/2560x1440.edid
new file mode 100644
index 0000000..0fd106d
--- /dev/null
+++ b/data/edids/2560x1440.edid
Binary files differ
diff --git a/manage.py b/manage.py
index d287c9c..8cbd18e 100755
--- a/manage.py
+++ b/manage.py
@@ -5,7 +5,7 @@ from plainbox.provider_manager import N_
setup(
name='plainbox-provider-checkbox',
namespace='com.canonical.certification',
- version="0.48.0",
+ version="0.49.0rc1",
description=N_("Checkbox provider"),
gettext_domain='plainbox-provider-checkbox',
strict=False, deprecated=False,
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7d289d8..d8a9a9e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -130,7 +130,6 @@ bin/samba_test
[type: gettext/python] bin/sleep_time_check
bin/sources_test
bin/spindown
-bin/storage_test
bin/tomcat_test
[type: gettext/python] bin/touchpad_driver_info
[type: gettext/python] bin/touchpad_test
diff --git a/units/audio/jobs.pxu b/units/audio/jobs.pxu
index 120a9bd..a661833 100644
--- a/units/audio/jobs.pxu
+++ b/units/audio/jobs.pxu
@@ -185,6 +185,37 @@ _steps:
_verification:
Did you hear the sound from the DisplayPort device?
+unit: template
+template-resource: graphics_card
+template-filter: graphics_card.prime_gpu_offload == 'Off'
+plugin: user-interact-verify
+category_id: com.canonical.plainbox::audio
+id: audio/{index}_playback_type-c_hdmi_{product_slug}
+imports: from com.canonical.plainbox import manifest
+estimated_duration: 30.0
+requires:
+ manifest.has_usb_type_c == 'True'
+ display.dp == 'supported'
+ device.category == 'AUDIO'
+ package.name == 'alsa-base'
+ package.name == 'gir1.2-gst-plugins-base-0.10' or package.name == 'gir1.2-gst-plugins-base-1.0'
+ package.name == 'pulseaudio-utils'
+flags: also-after-suspend-manual
+command:
+ audio_settings store --verbose --file=$PLAINBOX_SESSION_SHARE/pulseaudio_settings
+ audio_settings set --verbose --device=hdmi --volume=50
+ gst_pipeline_test -t 2 --device hdmi 'audiotestsrc wave=sine freq=512 ! audioconvert ! audioresample ! autoaudiosink'
+ EXIT_CODE=$?
+ audio_settings restore --verbose --file=$PLAINBOX_SESSION_SHARE/pulseaudio_settings
+ exit $EXIT_CODE
+_purpose:
+ HDMI audio via USB Type-C port interface verification
+_steps:
+ 1. Plug an external HDMI device with sound on a USB Type-C port using a "USB Typce-C to HDMI" adapter (Use only one HDMI/DisplayPort/Thunderbolt interface at a time for this test)
+ 2. Commence the test
+_verification:
+ Did you hear the sound from the HDMI device?
+
plugin: user-interact-verify
category_id: com.canonical.plainbox::audio
id: audio/playback_headphones
@@ -360,6 +391,8 @@ plugin: manual
category_id: com.canonical.plainbox::audio
id: audio/external-lineout
estimated_duration: 30.0
+requires:
+ dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving','All In One','All-In-One','AIO']
_description:
PURPOSE:
Check that external line out connection works correctly
@@ -376,6 +409,7 @@ category_id: com.canonical.plainbox::audio
id: audio/external-linein
estimated_duration: 120.0
requires:
+ dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving','All In One','All-In-One','AIO']
device.category == 'AUDIO'
package.name == 'alsa-base'
package.name == 'pulseaudio-utils'
diff --git a/units/bluetooth/jobs.pxu b/units/bluetooth/jobs.pxu
index f40a6e0..6160e76 100644
--- a/units/bluetooth/jobs.pxu
+++ b/units/bluetooth/jobs.pxu
@@ -82,11 +82,15 @@ _description:
PURPOSE:
This test will check that you can transfer information through a bluetooth connection
STEPS:
- 1. Make sure that you're able to browse the files in your mobile device
- 2. Copy a file from the computer to the mobile device
- 3. Copy a file from the mobile device to the computer
+ 1. Enable bluetooth on a target device (e.g. another laptop) and DUT (device under test)
+ 2. Launch bluetooth settings from menu bar
+ 3. Select target device from Device options for connection establishing
+ 4. Confirm PIN code on both DUT and target device
+ 5. Target device is connected
+ 7. Click target device in device list
+ 8. Click 'Send Files...' and choose a file to send in DUT
VERIFICATION:
- Were all files copied correctly?
+ Were file sent to target device ~/Downloads folder correctly?
plugin: user-interact-verify
category_id: com.canonical.plainbox::bluetooth
diff --git a/units/bluetooth/test-plan.pxu b/units/bluetooth/test-plan.pxu
index 1aca5be..2b1819b 100644
--- a/units/bluetooth/test-plan.pxu
+++ b/units/bluetooth/test-plan.pxu
@@ -15,10 +15,9 @@ _description:
Bluetooth tests (Manual)
include:
bluetooth/audio-a2dp certification-status=blocker
- bluetooth/HID
bluetooth4/HOGP-mouse certification-status=blocker
bluetooth4/HOGP-keyboard certification-status=blocker
-
+ bluetooth/file-transfer certification-status=non-blocker
id: bluetooth-cert-automated
unit: test plan
diff --git a/units/camera/jobs.pxu b/units/camera/jobs.pxu
index b1e4b9c..0605d18 100644
--- a/units/camera/jobs.pxu
+++ b/units/camera/jobs.pxu
@@ -15,6 +15,7 @@ template-unit: job
plugin: user-interact-verify
category_id: com.canonical.plainbox::camera
id: camera/display_{name}
+flags: also-after-suspend-manual
_summary: Webcam video display test for {product_slug}
estimated_duration: 120.0
depends: camera/detect
@@ -57,6 +58,7 @@ plugin: user-interact-verify
template-engine: jinja2
category_id: com.canonical.plainbox::camera
id: camera/still_{{ name }}
+flags: also-after-suspend-manual
_summary: Webcam still image capture test for {{ product_slug }}
estimated_duration: 120.0
depends: camera/detect
diff --git a/units/disk/jobs.pxu b/units/disk/jobs.pxu
index 3de5532..b2e4301 100644
--- a/units/disk/jobs.pxu
+++ b/units/disk/jobs.pxu
@@ -75,7 +75,7 @@ user: root
requires:
_summary: Disk I/O stress test for {product_slug}
_description: Take the path of the storage device and test is it a block device
-command: storage_test {name}
+command: storage_test.py {name}
unit: template
template-resource: device
diff --git a/units/ethernet/jobs.pxu b/units/ethernet/jobs.pxu
index b5f068a..103a7b3 100644
--- a/units/ethernet/jobs.pxu
+++ b/units/ethernet/jobs.pxu
@@ -2,7 +2,7 @@ plugin: shell
category_id: com.canonical.plainbox::ethernet
id: ethernet/detect
flags: also-after-suspend
-command: network_device_info.py detect
+command: network_device_info.py detect NETWORK
estimated_duration: 2.0
_summary:
Detect if at least one ethernet device is detected
@@ -18,14 +18,27 @@ id: ethernet/info_automated
requires:
executable.name == 'ip'
device.category == 'NETWORK'
-command: udev_resource -f NETWORK | awk "/interface: / { print \$2 }" | xargs -n 1 network_info
-estimated_duration: 30.0
+command: network_device_info.py detect NETWORK
+estimated_duration: 2.0
_summary:
Gather info on current state of network devices
_description:
Gathers some info on the current state of your network devices. If no devices
are found, the test will exit with an error.
+plugin: shell
+category_id: com.canonical.plainbox::ethernet
+id: ethernet/info_automated_server
+requires:
+ device.category == 'NETWORK'
+user: root
+command: network_device_info.py info NETWORK --no-nm
+estimated_duration: 2.0
+_summary:
+ Provide information about detected ethernet devices
+_description:
+ This job provides detailed information about detected ethernet devices.
+
plugin: user-interact-verify
category_id: com.canonical.plainbox::ethernet
id: ethernet/wired_connection
diff --git a/units/ethernet/test-plan.pxu b/units/ethernet/test-plan.pxu
index ec2310f..986c886 100644
--- a/units/ethernet/test-plan.pxu
+++ b/units/ethernet/test-plan.pxu
@@ -62,8 +62,7 @@ _name: Server Ethernet Tests
_description: Automated ethernet tests for server certification
estimated_duration: 4h
include:
- ethernet/detect
- ethernet/info_automated
+ ethernet/info_automated_server
ethernet/ethtool_info
ethernet/ethertool_check_.*
ethernet/multi_iperf3_nic_device.*
@@ -74,8 +73,7 @@ _name: Server Ethernet Tests (Speed Check Disabled)
_description: Automated ethernet tests for server certification without speed check
estimated_duration: 4h
include:
- ethernet/detect
- ethernet/info_automated
+ ethernet/info_automated_server
ethernet/ethtool_info
ethernet/ethertool_check_.*
ethernet/multi_iperf3_nic_underspeed_device.*
diff --git a/units/graphics/test-plan.pxu b/units/graphics/test-plan.pxu
index 9085cb8..4537dfd 100644
--- a/units/graphics/test-plan.pxu
+++ b/units/graphics/test-plan.pxu
@@ -51,8 +51,8 @@ include:
bootstrap_include:
graphics_card
nested_part:
- com.canonical.certification::graphics-discrete-gpu-cert-manual
com.canonical.certification::graphics-discrete-gpu-cert-automated
+ com.canonical.certification::graphics-discrete-gpu-cert-manual
id: graphics-discrete-gpu-cert-manual
unit: test plan
@@ -60,7 +60,6 @@ _name: Graphics tests (discrete GPU) (Manual)
_description:
Graphics tests (discrete GPU) (Manual)
include:
- graphics/2_auto_switch_card_.* certification-status=blocker
graphics/2_maximum_resolution_.* certification-status=blocker
graphics/2_glxgears_.* certification-status=blocker
graphics/2_rotation_.* certification-status=blocker
@@ -75,37 +74,58 @@ _name: Graphics tests (discrete GPU) (Automated)
_description:
Graphics tests (discrete GPU) (Automated)
include:
- graphics/2_valid_opengl_renderer_.* certification-status=blocker
- graphics/2_driver_version_.* certification-status=blocker
- graphics/2_compiz_check_.* certification-status=blocker
+ 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_minimum_resolution_.*
+ suspend/2_resolution_before_suspend_.*_auto certification-status=blocker
bootstrap_include:
graphics_card
-
id: after-suspend-graphics-integrated-gpu-cert-full
unit: test plan
_name: After suspend tests (integrated GPU)
_description: After suspend tests (integrated GPU)
include:
- graphics/1_auto_switch_card_.* certification-status=blocker
- suspend/1_resolution_before_suspend_.*_auto certification-status=blocker
+nested_part:
+ com.canonical.certification::after-suspend-graphics-integrated-gpu-cert-automated
+ com.canonical.certification::after-suspend-graphics-integrated-gpu-cert-manual
+
+id: after-suspend-graphics-integrated-gpu-cert-automated
+unit: test plan
+_name: After suspend tests (integrated GPU automated)
+_description: After suspend tests (integrated GPU automated)
+include:
+ graphics/1_auto_switch_card_.* certification-status=blocker
+ suspend/1_resolution_before_suspend_.*_auto certification-status=blocker
+ suspend/1_suspend_after_switch_to_card_.*_auto certification-status=blocker
# The following after suspend jobs will automatically select the right suspend job
# depending on the amount of graphic cards available on the SUT:
# suspend/suspend_advanced (one GPU)
# 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
- 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_driver_version_after_suspend_.*_auto certification-status=blocker
- suspend/1_resolution_after_suspend_.*_auto certification-status=blocker
- suspend/1_display_after_suspend_.*_auto certification-status=blocker
- suspend/1_glxgears_after_suspend_.*_auto certification-status=blocker
- suspend/1_video_after_suspend_.*_auto certification-status=blocker
- suspend/1_cycle_resolutions_after_suspend_.*_auto certification-status=non-blocker
+ 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_driver_version_after_suspend_.*_auto certification-status=blocker
+ suspend/1_resolution_after_suspend_.*_auto certification-status=blocker
+
+id: after-suspend-graphics-integrated-gpu-cert-manual
+unit: test plan
+_name: After suspend tests (integrated GPU manual)
+_description: After suspend tests (integrated GPU manual)
+include:
+ # The following after suspend jobs will automatically select the right suspend job
+ # depending on the amount of graphic cards available on the SUT:
+ # suspend/suspend_advanced (one GPU)
+ # or suspend/{{ index }}_suspend_after_switch_to_card_{{ product_slug }}_auto (two GPUs)
+ power-management/lid certification-status=blocker
+ power-management/lid_close certification-status=blocker
+ power-management/lid_open certification-status=blocker
+ suspend/1_display_after_suspend_.*_graphics certification-status=blocker
+ suspend/1_glxgears_after_suspend_.*_graphics certification-status=blocker
+ suspend/1_video_after_suspend_.*_graphics certification-status=blocker
+ suspend/1_cycle_resolutions_after_suspend_.*_graphics certification-status=non-blocker
suspend/1_xrandr_screens_after_suspend.tar.gz_auto
id: after-suspend-graphics-discrete-gpu-cert-full
@@ -113,19 +133,32 @@ unit: test plan
_name: After suspend tests (discrete GPU)
_description: After suspend tests (discrete GPU)
include:
- suspend/2_resolution_before_suspend_.*_auto certification-status=blocker
- 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
- suspend/2_compiz_check_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_.*_auto certification-status=blocker
- suspend/2_glxgears_after_suspend_.*_auto certification-status=blocker
- suspend/2_video_after_suspend_.*_auto certification-status=blocker
- suspend/2_cycle_resolutions_after_suspend_.*_auto certification-status=non-blocker
+nested_part:
+ com.canonical.certification::after-suspend-graphics-discrete-gpu-cert-automated
+ com.canonical.certification::after-suspend-graphics-discrete-gpu-cert-manual
+
+id: after-suspend-graphics-discrete-gpu-cert-automated
+unit: test plan
+_name: After suspend tests (discrete GPU automated)
+_description: After suspend tests (discrete GPU automated)
+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_driver_version_after_suspend_.*_auto certification-status=blocker
+ suspend/2_resolution_after_suspend_.*_auto certification-status=blocker
+
+id: after-suspend-graphics-discrete-gpu-cert-manual
+unit: test plan
+_name: After suspend tests (discrete GPU manual)
+_description: After suspend tests (discrete GPU manual)
+include:
+ suspend/2_display_after_suspend_.*_graphics certification-status=blocker
+ suspend/2_glxgears_after_suspend_.*_graphics certification-status=blocker
+ suspend/2_video_after_suspend_.*_graphics certification-status=blocker
+ suspend/2_cycle_resolutions_after_suspend_.*_graphics certification-status=non-blocker
suspend/2_xrandr_screens_after_suspend_.*.tar.gz_auto
- after-suspend-manual-monitor/2_dim_brightness_.* certification-status=blocker
id: graphics-integrated-gpu-cert-blockers
unit: test plan
@@ -175,10 +208,9 @@ include:
suspend/1_compiz_check_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_.*_auto certification-status=blocker
- suspend/1_glxgears_after_suspend_.*_auto certification-status=blocker
- suspend/1_video_after_suspend_.*_auto certification-status=blocker
- after-suspend-manual-monitor/1_dim_brightness_.* certification-status=blocker
+ suspend/1_display_after_suspend_.*_graphics certification-status=blocker
+ suspend/1_glxgears_after_suspend_.*_graphics certification-status=blocker
+ suspend/1_video_after_suspend_.*_graphics certification-status=blocker
id: after-suspend-graphics-discrete-gpu-cert-blockers
unit: test plan
@@ -186,11 +218,10 @@ _name: After suspend tests (discrete GPU, certification blockers only)
_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_.*_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_driver_version_after_suspend_.*_auto certification-status=blocker
suspend/2_resolution_after_suspend_.*_auto certification-status=blocker
- suspend/2_display_after_suspend_.*_auto certification-status=blocker
- suspend/2_glxgears_after_suspend_.*_auto certification-status=blocker
- suspend/2_video_after_suspend_.*_auto certification-status=blocker
- after-suspend-manual-monitor/2_dim_brightness_.* certification-status=blocker
+ suspend/2_display_after_suspend_.*_graphics certification-status=blocker
+ suspend/2_glxgears_after_suspend_.*_graphics certification-status=blocker
+ suspend/2_video_after_suspend_.*_graphics certification-status=blocker
diff --git a/units/info/jobs.pxu b/units/info/jobs.pxu
index 93ccd92..9ed5d33 100644
--- a/units/info/jobs.pxu
+++ b/units/info/jobs.pxu
@@ -103,7 +103,7 @@ category_id: com.canonical.plainbox::info
user: root
command:
if [[ -v SNAP ]]; then
- lsusb.py -f $SNAP/var/lib/usbutils/usb.ids
+ checkbox-support-lsusb -f $SNAP/checkbox-runtime/var/lib/usbutils/usb.ids
else
lsusb -vv | iconv -t 'utf-8' -c
fi
@@ -310,7 +310,7 @@ _description: Lists the device driver and version for all audio devices.
plugin: attachment
category_id: com.canonical.plainbox::info
id: info/network_devices
-command: network_device_info.py info
+command: network_device_info.py info NETWORK
estimated_duration: 0.550
_description: Provides information about network devices
@@ -452,3 +452,10 @@ command:
lstopo $PLAINBOX_SESSION_SHARE/lstopo_visual.png; \
[ -e "$PLAINBOX_SESSION_SHARE/lstopo_visual.png" ] && \
cat "$PLAINBOX_SESSION_SHARE/lstopo_visual.png"
+
+id: info/network-config
+plugin: attachment
+category_id: com.canonical.plainbox::info
+estimated_duration: 0.2
+_summary: attach network configuration
+command: network_configs
diff --git a/units/info/test-plan.pxu b/units/info/test-plan.pxu
index d293954..6e59a73 100644
--- a/units/info/test-plan.pxu
+++ b/units/info/test-plan.pxu
@@ -70,6 +70,7 @@ include:
efi_attachment
info/disk_partitions
info/hdparm_.*.txt
+ info/network-config
installer_debug.gz
kernel_cmdline_attachment
lsblk_attachment
diff --git a/units/keys/test-plan.pxu b/units/keys/test-plan.pxu
index c75a32a..38386ef 100644
--- a/units/keys/test-plan.pxu
+++ b/units/keys/test-plan.pxu
@@ -16,7 +16,6 @@ _description:
include:
keys/lock-screen certification-status=blocker
keys/super certification-status=blocker
- keys/battery-info certification-status=blocker
keys/brightness certification-status=blocker
keys/media-control certification-status=blocker
keys/mute certification-status=blocker
@@ -42,7 +41,6 @@ _description:
include:
after-suspend-manual-keys/lock-screen certification-status=blocker
after-suspend-manual-keys/super certification-status=blocker
- after-suspend-manual-keys/battery-info certification-status=blocker
after-suspend-manual-keys/brightness certification-status=blocker
after-suspend-manual-keys/media-control certification-status=blocker
after-suspend-manual-keys/mute certification-status=blocker
@@ -61,7 +59,6 @@ _description:
include:
keys/lock-screen certification-status=blocker
keys/super certification-status=blocker
- keys/battery-info certification-status=blocker
keys/brightness certification-status=blocker
keys/media-control certification-status=blocker
keys/mute certification-status=blocker
@@ -79,7 +76,6 @@ _description:
include:
after-suspend-manual-keys/lock-screen certification-status=blocker
after-suspend-manual-keys/super certification-status=blocker
- after-suspend-manual-keys/battery-info certification-status=blocker
after-suspend-manual-keys/brightness certification-status=blocker
after-suspend-manual-keys/media-control certification-status=blocker
after-suspend-manual-keys/mute certification-status=blocker
diff --git a/units/mediacard/jobs.pxu b/units/mediacard/jobs.pxu
index 01c0ad0..8ce16d6 100644
--- a/units/mediacard/jobs.pxu
+++ b/units/mediacard/jobs.pxu
@@ -156,6 +156,20 @@ _description:
system under test has a memory card device plugged in prior to checkbox execution.
It is intended for SRU automated testing.
+plugin: shell
+category_id: com.canonical.plainbox::mediacard
+id: mediacard/sd-preinserted-server
+estimated_duration: 30.0
+user: root
+flags: preserve-cwd
+command: removable_storage_test -s 268400000 --memorycard -l sdio usb scsi && removable_storage_test --memorycard sdio usb scsi
+requires:
+ package.name == 'udisks2' or snap.name == 'udisks2'
+_summary: Automated test of SD Card reading & writing (udisk2) for servers
+_description:
+ This is a fully automated version of mediacard/sd-automated and assumes that the
+ system under test has a memory card device plugged in prior to checkbox execution.
+
plugin: user-interact
template-engine: jinja2
category_id: com.canonical.plainbox::mediacard
diff --git a/units/mediacard/test-plan.pxu b/units/mediacard/test-plan.pxu
index 58d3f07..2ef8a01 100644
--- a/units/mediacard/test-plan.pxu
+++ b/units/mediacard/test-plan.pxu
@@ -14,9 +14,6 @@ _name: Mediacard tests (Manual)
_description:
Mediacard tests (Manual)
include:
- mediacard/sd-insert certification-status=blocker
- mediacard/sd-storage certification-status=blocker
- mediacard/sd-remove certification-status=blocker
mediacard/sdhc-insert certification-status=blocker
mediacard/sdhc-storage certification-status=blocker
mediacard/sdhc-remove certification-status=blocker
@@ -34,9 +31,6 @@ unit: test plan
_name: Mediacard tests (after suspend)
_description: Mediacard tests (after suspend)
include:
- suspend/sd-insert-after-suspend certification-status=blocker
- suspend/sd-storage-after-suspend certification-status=blocker
- suspend/sd-remove-after-suspend certification-status=blocker
suspend/sdhc-insert-after-suspend certification-status=blocker
suspend/sdhc-storage-after-suspend certification-status=blocker
suspend/sdhc-remove-after-suspend certification-status=blocker
@@ -46,9 +40,6 @@ unit: test plan
_name: Mediacard tests (certification blockers only)
_description: Mediacard tests (certification blockers only)
include:
- mediacard/sd-insert certification-status=blocker
- mediacard/sd-storage certification-status=blocker
- mediacard/sd-remove certification-status=blocker
mediacard/sdhc-insert certification-status=blocker
mediacard/sdhc-storage certification-status=blocker
mediacard/sdhc-remove certification-status=blocker
@@ -58,9 +49,6 @@ unit: test plan
_name: Mediacard tests (after suspend, certification blockers only)
_description: Mediacard tests (after suspend, certification blockers only)
include:
- suspend/sd-insert-after-suspend certification-status=blocker
- suspend/sd-storage-after-suspend certification-status=blocker
- suspend/sd-remove-after-suspend certification-status=blocker
suspend/sdhc-insert-after-suspend certification-status=blocker
suspend/sdhc-storage-after-suspend certification-status=blocker
suspend/sdhc-remove-after-suspend certification-status=blocker
diff --git a/units/miscellanea/jobs.pxu b/units/miscellanea/jobs.pxu
index cccec4c..ca726a6 100644
--- a/units/miscellanea/jobs.pxu
+++ b/units/miscellanea/jobs.pxu
@@ -434,3 +434,21 @@ user: root
command:
SOSFILE=`ls -t $PLAINBOX_SESSION_SHARE/sosreport*xz | head -1`; [ -e ${SOSFILE} ] && cat $SOSFILE
_summary: Attach the baseline sosreport file
+
+plugin: shell
+category_id: com.canonical.plainbox::miscellanea
+estimated_duration: 0.2
+id: miscellanea/cpuid
+user: root
+requires: cpuinfo.platform in ("i386", "x86_64")
+command: cpuid.py
+_summary: Attempt to identify CPU family (x86/amd64 only)
+_description: Attempts to identify the CPU family of an x86/amd64 processor
+
+plugin: shell
+category_id: com.canonical.plainbox::miscellanea
+estimated_duration: 240
+id: miscellanea/fan_stress_reaction
+command: fan_reaction_test.py
+_summary: Check if system fans react to CPU load
+_description: Check if system fans react to CPU load
diff --git a/units/miscellanea/test-plan.pxu b/units/miscellanea/test-plan.pxu
index 9c17b3a..b63d34f 100644
--- a/units/miscellanea/test-plan.pxu
+++ b/units/miscellanea/test-plan.pxu
@@ -34,6 +34,7 @@ include:
miscellanea/oops certification-status=blocker
miscellanea/oops_results.log
miscellanea/dmitest_client
+ miscellanea/fan_stress_reaction
bootstrap_include:
fwts
@@ -61,6 +62,7 @@ _description:
Miscellaneous server tests (log checks, dmi data, etc)
mandatory_include:
miscellanea/submission-resources
+ miscellanea/cpuid
miscellanea/get_make_and_model
miscellanea/get_maas_version certification-status=blocker
miscellanea/efi_boot_mode certification-status=blocker
diff --git a/units/monitor/jobs.pxu b/units/monitor/jobs.pxu
index 255d74c..54a86c9 100644
--- a/units/monitor/jobs.pxu
+++ b/units/monitor/jobs.pxu
@@ -7,12 +7,15 @@ flags: also-after-suspend-manual
plugin: manual
category_id: com.canonical.plainbox::monitor
_purpose:
- This test will check your VGA port.
+ This test will check your VGA port as a monitor interconnect for {vendor} {product}.
_steps:
Skip this test if your system does not have a VGA port.
1. Connect a display (if not already connected) to the VGA port on your system
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
_verification:
- Was the desktop displayed correctly on both screens?
+ Was the desktop displayed correctly with {vendor} {product} on the VGA-connected
+ screen in every mode?
unit: template
template-resource: graphics_card
@@ -23,12 +26,15 @@ flags: also-after-suspend-manual
plugin: manual
category_id: com.canonical.plainbox::monitor
_purpose:
- This test will check your DVI port.
+ This test will check your DVI port as a monitor interconnect for {vendor} {product}.
_steps:
Skip this test if your system does not have a DVI port.
1. Connect a display (if not already connected) to the DVI port on your system
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
_verification:
- Was the desktop displayed correctly on both screens?
+ Was the desktop displayed correctly with {vendor} {product} on the DVI-connected
+ screen in every mode?
unit: template
template-resource: graphics_card
@@ -39,12 +45,15 @@ flags: also-after-suspend-manual
plugin: manual
category_id: com.canonical.plainbox::monitor
_purpose:
- This test will check your DisplayPort port.
+ This test will check your DisplayPort port as a monitor interconnect for {vendor} {product}.
_steps:
Skip this test if your system does not have a DisplayPort port.
1. Connect a display (if not already connected) to the DisplayPort port on your system
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
_verification:
- Was the desktop displayed correctly on both screens?
+ Was the desktop displayed correctly with {vendor} {product} on the DisplayPort-connected
+ screen in every mode?
unit: template
template-resource: graphics_card
@@ -55,12 +64,15 @@ flags: also-after-suspend-manual
plugin: manual
category_id: com.canonical.plainbox::monitor
_purpose:
- This test will check your HDMI port.
+ This test will check your HDMI port as a monitor interconnect for {vendor} {product}.
_steps:
Skip this test if your system does not have a HDMI port.
1. Connect a display (if not already connected) to the HDMI port on your system
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
_verification:
- Was the desktop displayed correctly on both screens?
+ Was the desktop displayed correctly with {vendor} {product} on the HDMI-connected
+ screen in every mode?
unit: template
template-resource: graphics_card
@@ -70,12 +82,15 @@ requires: display.svideo == 'supported'
plugin: manual
category_id: com.canonical.plainbox::monitor
_purpose:
- This test will check your S-VIDEO port.
+ This test will check your S-VIDEO port as a monitor interconnect for {vendor} {product}.
_steps:
Skip this test if your system does not have a S-VIDEO port.
1. Connect a display (if not already connected) to the S-VIDEO port on your system
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
_verification:
- Was the desktop displayed correctly on both screens?
+ Was the desktop displayed correctly with {vendor} {product} on the S-VIDEO-connected
+ screen in every mode?
unit: template
template-resource: graphics_card
@@ -85,12 +100,15 @@ requires: display.rca == 'supported'
plugin: manual
category_id: com.canonical.plainbox::monitor
_purpose:
- This test will check your RCA port.
+ This test will check your RCA port as a monitor interconnect for {vendor} {product}.
_steps:
Skip this test if your system does not have a RCA port.
1. Connect a display (if not already connected) to the RCA port on your system
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
_verification:
- Was the desktop displayed correctly on both screens?
+ Was the desktop displayed correctly with {vendor} {product} on the RCA-connected
+ screen in every mode?
unit: template
template-resource: graphics_card
@@ -212,6 +230,50 @@ _verification:
Was the desktop displayed correctly with {vendor} {product} on the screen
connected using a "USB Type-C to DisplayPort" adapter in every mode?
+unit: template
+template-resource: graphics_card
+id: monitor/{index}_type-c_hdmi_{product_slug}
+template-filter: graphics_card.prime_gpu_offload == 'Off'
+imports: from com.canonical.plainbox import manifest
+requires: manifest.has_usb_type_c == 'True'
+flags: also-after-suspend-manual
+estimated_duration: 15.0
+plugin: manual
+category_id: com.canonical.plainbox::monitor
+_summary: Display connected via HDMI using an USB Type-C port for {vendor} {product}
+_purpose:
+ This test will check the connection of a screen using a "USB Type-C to HDMI" adapter for {vendor} {product}.
+_steps:
+ 1. Connect a display (if not already connected) to the USB Type-C port on
+ your system using a "USB Type-C to HDMI" adapter
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
+_verification:
+ Was the desktop displayed correctly with {vendor} {product} on the screen
+ connected using a "USB Type-C to HDMI" adapter in every mode?
+
+unit: template
+template-resource: graphics_card
+id: monitor/{index}_type-c_vga_{product_slug}
+template-filter: graphics_card.prime_gpu_offload == 'Off'
+imports: from com.canonical.plainbox import manifest
+requires: manifest.has_usb_type_c == 'True'
+flags: also-after-suspend-manual
+estimated_duration: 15.0
+plugin: manual
+category_id: com.canonical.plainbox::monitor
+_summary: Display connected via VGA using an USB Type-C port for {vendor} {product}
+_purpose:
+ This test will check the connection of a screen using a "USB Type-C to VGA" adapter for {vendor} {product}.
+_steps:
+ 1. Connect a display (if not already connected) to the USB Type-C port on
+ your system using a "USB Type-C to VGA" adapter
+ 2. Switch display modes between in your Display Settings, check if it can be
+ set to mirrored, extended, displayed on external or onboard only
+_verification:
+ Was the desktop displayed correctly with {vendor} {product} on the screen
+ connected using a "USB Type-C to VGA" adapter in every mode?
+
id: monitor/type-c-to-hdmi
imports: from com.canonical.plainbox import manifest
requires: manifest.has_usb_type_c == 'True'
diff --git a/units/monitor/test-plan.pxu b/units/monitor/test-plan.pxu
index fbed317..2e635cf 100644
--- a/units/monitor/test-plan.pxu
+++ b/units/monitor/test-plan.pxu
@@ -27,19 +27,20 @@ include:
monitor/1_powersaving_.* certification-status=blocker
monitor/1_dim_brightness_.* certification-status=blocker
monitor/1_displayport_.* certification-status=blocker
- monitor/1_type-c_displayport_.* certification-status=blocker
- monitor/type-c-to-hdmi certification-status=blocker
- monitor/type-c-to-vga certification-status=blocker
audio/1_playback_displayport_.* certification-status=blocker
+ monitor/1_type-c_displayport_.* certification-status=blocker
audio/1_playback_type-c_displayport_.* certification-status=blocker
+ monitor/1_type-c_hdmi_.* certification-status=blocker
+ audio/1_playback_type-c_hdmi_.* certification-status=blocker
+ monitor/1_type-c_vga_.* certification-status=blocker
monitor/1_dvi_.* certification-status=blocker
monitor/1_hdmi_.* certification-status=blocker
audio/1_playback_hdmi_.* certification-status=blocker
monitor/1_thunderbolt_.* certification-status=blocker
- monitor/1_thunderbolt3_.* certification-status=non-blocker
audio/1_playback_thunderbolt_.* certification-status=blocker
- audio/1_playback_thunderbolt3_.* certification-status=non-blocker
thunderbolt/daisy-chain certification-status=blocker
+ monitor/1_thunderbolt3_.* certification-status=non-blocker
+ audio/1_playback_thunderbolt3_.* certification-status=non-blocker
thunderbolt3/daisy-chain certification-status=non-blocker
monitor/1_vga_.* certification-status=blocker
monitor/1_multi-head_.* certification-status=blocker
@@ -52,25 +53,26 @@ _name: Monitor tests (after manual suspend, integrated GPU) (Manual)
_description:
Monitor tests (after manual suspend, integrated GPU) (Manual)
include:
- (after-suspend-manual-)?monitor/1_powersaving_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_dim_brightness_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_displayport_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_type-c_displayport_.* certification-status=blocker
- (after-suspend-manual-)?monitor/type-c-to-hdmi certification-status=blocker
- (after-suspend-manual-)?monitor/type-c-to-vga certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_displayport_.* certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_type-c_displayport_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_dvi_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_hdmi_.* certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_hdmi_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_thunderbolt_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_thunderbolt3_.* certification-status=non-blocker
- (after-suspend-manual-)?audio/1_playback_thunderbolt_.* certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_thunderbolt3_.* certification-status=non-blocker
- (after-suspend-manual-)?thunderbolt/daisy-chain certification-status=blocker
- (after-suspend-manual-)?thunderbolt3/daisy-chain certification-status=non-blocker
- (after-suspend-manual-)?monitor/1_vga_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_multi-head_.* certification-status=blocker
+ after-suspend-manual-monitor/1_powersaving_.* certification-status=blocker
+ after-suspend-manual-monitor/1_dim_brightness_.* certification-status=blocker
+ after-suspend-manual-monitor/1_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/1_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/1_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/1_type-c_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/1_dvi_.* certification-status=blocker
+ after-suspend-manual-monitor/1_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/1_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-thunderbolt/daisy-chain certification-status=blocker
+ after-suspend-manual-monitor/1_thunderbolt3_.* certification-status=non-blocker
+ after-suspend-manual-audio/1_playback_thunderbolt3_.* certification-status=non-blocker
+ after-suspend-manual-thunderbolt3/daisy-chain certification-status=non-blocker
+ after-suspend-manual-monitor/1_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/1_multi-head_.* certification-status=blocker
bootstrap_include:
graphics_card
@@ -105,21 +107,63 @@ include:
monitor/2_powersaving_.* certification-status=blocker
monitor/2_dim_brightness_.* certification-status=blocker
monitor/2_displayport_.* certification-status=blocker
- monitor/2_type-c_displayport_.* certification-status=blocker
audio/2_playback_displayport_.* certification-status=blocker
+ monitor/2_type-c_displayport_.* certification-status=blocker
audio/2_playback_type-c_displayport_.* certification-status=blocker
+ monitor/2_type-c_hdmi_.* certification-status=blocker
+ audio/2_playback_type-c_hdmi_.* certification-status=blocker
+ monitor/2_type-c_vga_.* certification-status=blocker
monitor/2_dvi_.* certification-status=blocker
monitor/2_hdmi_.* certification-status=blocker
audio/2_playback_hdmi_.* certification-status=blocker
monitor/2_thunderbolt_.* certification-status=blocker
- monitor/2_thunderbolt3_.* certification-status=non-blocker
audio/2_playback_thunderbolt_.* certification-status=blocker
+ monitor/2_thunderbolt3_.* certification-status=non-blocker
audio/2_playback_thunderbolt3_.* certification-status=non-blocker
monitor/2_vga_.* certification-status=blocker
monitor/2_multi-head_.* certification-status=blocker
bootstrap_include:
graphics_card
+
+id: after-suspend-monitor-discrete-gpu-cert-full
+unit: test plan
+_name: Monitor tests (after suspend, integrated GPU)
+_description: Monitor tests (after suspend, integrated GPU)
+include:
+nested_part:
+ after-suspend-manual-monitor-discrete-gpu-cert-manual
+
+
+id: after-suspend-manual-monitor-discrete-gpu-cert-manual
+unit: test plan
+_name: Monitor tests (after manual suspend, discrete GPU) (Manual)
+_description:
+ Monitor tests (after manual suspend, discrete GPU) (Manual)
+include:
+ after-suspend-manual-monitor/2_powersaving_.* certification-status=blocker
+ after-suspend-manual-monitor/2_dim_brightness_.* certification-status=blocker
+ after-suspend-manual-monitor/2_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/2_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/2_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/2_type-c_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/2_dvi_.* certification-status=blocker
+ after-suspend-manual-monitor/2_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/2_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-thunderbolt/daisy-chain certification-status=blocker
+ after-suspend-manual-monitor/2_thunderbolt3_.* certification-status=non-blocker
+ after-suspend-manual-audio/2_playback_thunderbolt3_.* certification-status=non-blocker
+ after-suspend-manual-thunderbolt3/daisy-chain certification-status=non-blocker
+ after-suspend-manual-monitor/2_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/2_multi-head_.* certification-status=blocker
+bootstrap_include:
+ graphics_card
+
id: monitor-discrete-gpu-cert-automated
unit: test plan
_name: Monitor tests (discrete GPU) (Automated)
@@ -138,11 +182,12 @@ include:
monitor/1_powersaving_.* certification-status=blocker
monitor/1_dim_brightness_.* certification-status=blocker
monitor/1_displayport_.* certification-status=blocker
- monitor/1_type-c_displayport_.* certification-status=blocker
- monitor/type-c-to-hdmi certification-status=blocker
- monitor/type-c-to-vga certification-status=blocker
audio/1_playback_displayport_.* certification-status=blocker
+ monitor/1_type-c_displayport_.* certification-status=blocker
audio/1_playback_type-c_displayport_.* certification-status=blocker
+ monitor/1_type-c_hdmi_.* certification-status=blocker
+ audio/1_playback_type-c_hdmi_.* certification-status=blocker
+ monitor/1_type-c_vga_.* certification-status=blocker
monitor/1_dvi_.* certification-status=blocker
monitor/1_hdmi_.* certification-status=blocker
audio/1_playback_hdmi_.* certification-status=blocker
@@ -159,22 +204,23 @@ unit: test plan
_name: Monitor tests (after manual suspend, integrated GPU, certification blockers only)
_description: Monitor tests (after manual suspend, integrated GPU, certification blockers only)
include:
- (after-suspend-manual-)?monitor/1_powersaving_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_dim_brightness_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_displayport_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_type-c_displayport_.* certification-status=blocker
- (after-suspend-manual-)?monitor/type-c-to-hdmi certification-status=blocker
- (after-suspend-manual-)?monitor/type-c-to-vga certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_displayport_.* certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_type-c_displayport_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_dvi_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_hdmi_.* certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_hdmi_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_thunderbolt_.* certification-status=blocker
- (after-suspend-manual-)?audio/1_playback_thunderbolt_.* certification-status=blocker
- (after-suspend-manual-)?thunderbolt/daisy-chain certification-status=blocker
- (after-suspend-manual-)?monitor/1_vga_.* certification-status=blocker
- (after-suspend-manual-)?monitor/1_multi-head_.* certification-status=blocker
+ after-suspend-manual-monitor/1_powersaving_.* certification-status=blocker
+ after-suspend-manual-monitor/1_dim_brightness_.* certification-status=blocker
+ after-suspend-manual-monitor/1_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/1_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/1_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/1_type-c_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/1_dvi_.* certification-status=blocker
+ after-suspend-manual-monitor/1_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/1_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-audio/1_playback_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-thunderbolt/daisy-chain certification-status=blocker
+ after-suspend-manual-monitor/1_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/1_multi-head_.* certification-status=blocker
bootstrap_include:
graphics_card
@@ -186,9 +232,12 @@ include:
monitor/2_powersaving_.* certification-status=blocker
monitor/2_dim_brightness_.* certification-status=blocker
monitor/2_displayport_.* certification-status=blocker
- monitor/2_type-c_displayport_.* certification-status=blocker
audio/2_playback_displayport_.* certification-status=blocker
+ monitor/2_type-c_displayport_.* certification-status=blocker
audio/2_playback_type-c_displayport_.* certification-status=blocker
+ monitor/2_type-c_hdmi_.* certification-status=blocker
+ audio/2_playback_type-c_hdmi_.* certification-status=blocker
+ monitor/2_type-c_vga_.* certification-status=blocker
monitor/2_dvi_.* certification-status=blocker
monitor/2_hdmi_.* certification-status=blocker
audio/2_playback_hdmi_.* certification-status=blocker
@@ -198,3 +247,30 @@ include:
monitor/2_multi-head_.* certification-status=blocker
bootstrap_include:
graphics_card
+
+id: after-suspend-manual-monitor-discrete-gpu-cert-blockers
+unit: test plan
+_name: Monitor tests (after manual suspend, discrete GPU, certification blockers only)
+_description: Monitor tests (after manual suspend, discrete GPU, certification blockers only)
+include:
+ after-suspend-manual-monitor/2_powersaving_.* certification-status=blocker
+ after-suspend-manual-monitor/2_dim_brightness_.* certification-status=blocker
+ after-suspend-manual-monitor/2_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/2_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_type-c_displayport_.* certification-status=blocker
+ after-suspend-manual-monitor/2_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_type-c_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/2_type-c_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/2_dvi_.* certification-status=blocker
+ after-suspend-manual-monitor/2_hdmi_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_hdmi_.* certification-status=blocker
+ after-suspend-manual-monitor/2_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-audio/2_playback_thunderbolt_.* certification-status=blocker
+ after-suspend-manual-thunderbolt/daisy-chain certification-status=blocker
+ after-suspend-manual-monitor/2_vga_.* certification-status=blocker
+ after-suspend-manual-monitor/2_multi-head_.* certification-status=blocker
+bootstrap_include:
+ graphics_card
+
+
diff --git a/units/networking/jobs.pxu b/units/networking/jobs.pxu
index 360aaf2..c5f5c9f 100644
--- a/units/networking/jobs.pxu
+++ b/units/networking/jobs.pxu
@@ -14,7 +14,8 @@ category_id: com.canonical.plainbox::networking
id: networking/info_device{__index__}_{interface}
_summary: Network Information of device {__index__} ({interface})
estimated_duration: 1.0
-command: network_info {interface} | zenity --text-info --title="{interface}"
+command:
+ network_device_info.py info NETWORK --interface {interface} | zenity --text-info --title="{interface}"
_description:
PURPOSE:
This test will check the network device {__index__} ({interface})
diff --git a/units/networking/test-plan.pxu b/units/networking/test-plan.pxu
index 5a7da04..1abee61 100644
--- a/units/networking/test-plan.pxu
+++ b/units/networking/test-plan.pxu
@@ -35,6 +35,7 @@ include:
networking/gateway_ping certification-status=blocker
networking/info_device.* certification-status=blocker
networking/ntp certification-status=blocker
+ ethernet/hotplug-.* certification-status=blocker
bootstrap_include:
device
diff --git a/units/optical/jobs.pxu b/units/optical/jobs.pxu
index 707c02d..53a2877 100644
--- a/units/optical/jobs.pxu
+++ b/units/optical/jobs.pxu
@@ -16,6 +16,7 @@ id: optical/read_{name}
estimated_duration: 120.0
user: root
command: optical_read_test /dev/{name}
+flags: also-after-suspend-manual
_description:
PURPOSE:
This test will check your {product} device's ability to read CD media
@@ -164,6 +165,7 @@ requires:
optical_drive_{name}.bd_read == "supported"
user: root
command: optical_read_test /dev/{name}
+flags: also-after-suspend-manual
_description:
PURPOSE:
This test will check your {product} device's ability to read Blu-Ray (BD) media
diff --git a/units/optical/test-plan.pxu b/units/optical/test-plan.pxu
index 2e53d79..f927f00 100644
--- a/units/optical/test-plan.pxu
+++ b/units/optical/test-plan.pxu
@@ -31,6 +31,17 @@ include:
bootstrap_include:
device
+id: after-suspend-optical-cert-full
+unit: test plan
+_name: Optical drive tests (after suspend)
+_description:
+ Optical drive tests (after suspend)
+include:
+ optical/detect certification-status=blocker
+ after-suspend-manual-optical/read_.* certification-status=blocker
+ after-suspend-manual-optical/bluray-read_.* certification-status=blocker
+bootstrap_include:
+ device
id: optical-cert-blockers
unit: test plan
@@ -42,3 +53,15 @@ include:
optical/bluray-read_.* certification-status=blocker
bootstrap_include:
device
+
+id: after-suspend-optical-cert-blockers
+unit: test plan
+_name: Optical drive tests (certification blockers only)
+_description: Optical drive tests (certification blockers only)
+include:
+ optical/detect certification-status=blocker
+ after-suspend-manual-optical/read_.* certification-status=blocker
+ after-suspend-manual-optical/bluray-read_.* certification-status=blocker
+bootstrap_include:
+ device
+
diff --git a/units/suspend/suspend-graphics.pxu b/units/suspend/suspend-graphics.pxu
index be820a7..d4fe923 100644
--- a/units/suspend/suspend-graphics.pxu
+++ b/units/suspend/suspend-graphics.pxu
@@ -14,7 +14,7 @@ command:
unit: template
template-resource: graphics_card
template-filter: graphics_card.prime_gpu_offload == 'Off'
-plugin: user-interact-verify
+plugin: shell
category_id: com.canonical.plainbox::suspend
id: suspend/{index}_suspend_after_switch_to_card_{product_slug}_auto
requires:
@@ -24,27 +24,12 @@ after: graphics/{index}_auto_switch_card_{product_slug}
user: root
environ: PLAINBOX_SESSION_SHARE
command:
- if type -P fwts >/dev/null; then
- echo "Calling fwts"
- set -o pipefail; checkbox-support-fwts_test -f none -l $PLAINBOX_SESSION_SHARE/{index}_suspend_single -s s3 --s3-sleep-delay=30 --s3-device-check --s3-device-check-delay=45 | tee $PLAINBOX_SESSION_SHARE/{index}_suspend_single_times.log
- else
- echo "Calling sleep_test"
- set -o pipefail; sleep_test -p | tee $PLAINBOX_SESSION_SHARE/{index}_suspend_single_times.log
+ if [[ -v SNAP ]]; then
+ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$SNAP/usr/lib/fwts"
fi
-estimated_duration: 90.0
-_summary: Test suspend/resume after switching to {vendor} {product}
-_description:
- PURPOSE:
- This test will check suspend and resume after switching to {vendor} {product} graphics card.
- STEPS:
- 1. Ensure you have switched to {vendor} {product} graphics card.
- 2. Click "Test" and your system will suspend for about 30 - 60 seconds
- 3. Observe the Power LED to see if it blinks or changes color during suspend
- 4. If your system does not wake itself up after 60 seconds, please press the power button momentarily to wake the system manually
- 5. If your system fails to wake at all and must be rebooted, restart System Testing after reboot and mark this test as Failed
- VERIFICATION:
- Did your system suspend and resume correctly after switching to {vendor} {product} graphics card?
- (NOTE: Please only consider whether the system successfully suspended and resumed. Power/Suspend LED verification will occur after this test is completed.)
+ set -o pipefail; checkbox-support-fwts_test -f none -l $PLAINBOX_SESSION_SHARE/{index}_suspend_single -s s3 --s3-sleep-delay=30 --s3-device-check --s3-device-check-delay=45 | tee $PLAINBOX_SESSION_SHARE/{index}_suspend_single_times.log
+estimated_duration: 1m30s
+_summary: Test auto suspend/resume after switching to {vendor} {product}
unit: template
template-resource: graphics_card
@@ -72,7 +57,7 @@ template-filter: graphics_card.prime_gpu_offload == 'Off'
template-engine: jinja2
plugin: manual
category_id: com.canonical.plainbox::suspend
-id: suspend/{{ index }}_display_after_suspend_{{ product_slug }}_auto
+id: suspend/{{ index }}_display_after_suspend_{{ product_slug }}_graphics
depends:
{%- if gpu_count > "1" %}
suspend/{{ index }}_suspend_after_switch_to_card_{{ product_slug }}_auto
@@ -94,7 +79,7 @@ template-filter: graphics_card.prime_gpu_offload == 'Off'
template-engine: jinja2
plugin: user-interact-verify
category_id: com.canonical.plainbox::suspend
-id: suspend/{{ index }}_cycle_resolutions_after_suspend_{{ product_slug }}_auto
+id: suspend/{{ index }}_cycle_resolutions_after_suspend_{{ product_slug }}_graphics
requires: package.name == 'xorg'
depends:
{%- if gpu_count > "1" %}
@@ -120,7 +105,7 @@ template-filter: graphics_card.prime_gpu_offload == 'Off'
plugin: attachment
category_id: com.canonical.plainbox::suspend
id: suspend/{index}_xrandr_screens_after_suspend.tar.gz_auto
-depends: suspend/{index}_cycle_resolutions_after_suspend_{product_slug}_auto
+depends: suspend/{index}_cycle_resolutions_after_suspend_{product_slug}_graphics
command: [ -f $PLAINBOX_SESSION_SHARE/{index}_xrandr_screens_after_suspend.tgz ] && cat $PLAINBOX_SESSION_SHARE/{index}_xrandr_screens_after_suspend.tgz
_description: This attaches screenshots from the suspend/cycle_resolutions_after_suspend test to the results submission.
@@ -150,7 +135,7 @@ template-resource: graphics_card
template-engine: jinja2
plugin: user-interact-verify
category_id: com.canonical.plainbox::suspend
-id: suspend/{{ index }}_glxgears_after_suspend_{{ product_slug }}_auto
+id: suspend/{{ index }}_glxgears_after_suspend_{{ product_slug }}_graphics
depends:
{%- if gpu_count > "1" %}
suspend/{{ index }}_suspend_after_switch_to_card_{{ product_slug }}_auto
@@ -176,7 +161,7 @@ _description:
unit: template
template-resource: graphics_card
template-engine: jinja2
-id: suspend/{{ index }}_video_after_suspend_{{ product_slug }}_auto
+id: suspend/{{ index }}_video_after_suspend_{{ product_slug }}_graphics
depends:
{%- if gpu_count > "1" %}
suspend/{{ index }}_suspend_after_switch_to_card_{{ product_slug }}_auto
diff --git a/units/thunderbolt/jobs.pxu b/units/thunderbolt/jobs.pxu
index 14fa3f8..74481fe 100644
--- a/units/thunderbolt/jobs.pxu
+++ b/units/thunderbolt/jobs.pxu
@@ -5,6 +5,11 @@ imports: from com.canonical.plainbox import manifest
requires: manifest.has_thunderbolt == 'True'
estimated_duration: 20.0
command: removable_storage_watcher insert --timeout 40 scsi
+_siblings: [
+ { "id": "after-suspend-manual-thunderbolt/insert",
+ "_summary": "thunderbolt/insert after suspend",
+ "depends": "suspend/suspend_advanced"}
+ ]
_summary: Storage insert detection on Thunderbolt
_description:
PURPOSE:
@@ -27,6 +32,11 @@ requires: manifest.has_thunderbolt == 'True'
depends: thunderbolt/insert
estimated_duration: 45.0
command: removable_storage_test -s 268400000 scsi
+_siblings: [
+ { "id": "after-suspend-manual-thunderbolt/storage-test",
+ "_summary": "thunderbolt/storage-test after suspend",
+ "depends": "after-suspend-manual-thunderbolt/insert"}
+ ]
_summary: Storage test on Thunderbolt
_description:
This is an automated test which performs read/write operations on an attached
@@ -41,6 +51,11 @@ depends: thunderbolt/insert
estimated_duration: 10.0
command: removable_storage_watcher remove scsi
_summary: Storage removal detection on Thunderbolt
+_siblings: [
+ { "id": "after-suspend-manual-thunderbolt/remove",
+ "_summary": "thunderbolt/remove after suspend",
+ "depends": "after-suspend-manual-thunderbolt/insert"}
+ ]
_description:
PURPOSE:
This test will check the system can detect the removal of a Thunderbolt HDD
@@ -82,6 +97,11 @@ imports: from com.canonical.plainbox import manifest
requires: manifest.has_thunderbolt3 == 'True'
estimated_duration: 20.0
command: removable_storage_watcher insert --timeout 40 scsi
+_siblings: [
+ { "id": "after-suspend-manual-thunderbolt3/insert",
+ "_summary": "thunderbolt3/insert after suspend",
+ "depends": "suspend/suspend_advanced"}
+ ]
_summary: Storage insert detection on Thunderbolt 3 port
_description:
PURPOSE:
@@ -104,6 +124,11 @@ requires: manifest.has_thunderbolt3 == 'True'
depends: thunderbolt3/insert
estimated_duration: 45.0
command: removable_storage_test -s 268400000 scsi
+_siblings: [
+ { "id": "after-suspend-manual-thunderbolt3/storage-test",
+ "_summary": "thunderbolt3/storage-test after suspend",
+ "depends": "after-suspend-manual-thunderbolt3/insert"}
+ ]
_summary: Storage test on Thunderbolt 3
_description:
This is an automated test which performs read/write operations on an attached
@@ -117,6 +142,11 @@ requires: manifest.has_thunderbolt3 == 'True'
depends: thunderbolt3/insert
estimated_duration: 10.0
command: removable_storage_watcher remove scsi
+_siblings: [
+ { "id": "after-suspend-manual-thunderbolt3/remove",
+ "_summary": "thunderbolt3/remove after suspend",
+ "depends": "after-suspend-manual-thunderbolt3/insert"}
+ ]
_summary: Storage removal detection on Thunderbolt 3 port
_description:
PURPOSE:
diff --git a/units/thunderbolt/test-plan.pxu b/units/thunderbolt/test-plan.pxu
index a0efcda..75aa0ac 100644
--- a/units/thunderbolt/test-plan.pxu
+++ b/units/thunderbolt/test-plan.pxu
@@ -8,18 +8,27 @@ nested_part:
com.canonical.certification::thunderbolt-cert-manual
com.canonical.certification::thunderbolt-cert-automated
+id: after-suspend-thunderbolt-cert-full
+unit: test plan
+_name: Thunderbolt tests
+_description:
+ Thunderbolt tests
+include:
+nested_part:
+ com.canonical.certification::after-suspend-thunderbolt-cert-manual
+
id: thunderbolt-cert-manual
unit: test plan
_name: Thunderbolt tests (Manual)
_description:
Thunderbolt tests (Manual)
include:
- thunderbolt/insert certification-status=non-blocker
- thunderbolt/storage-test certification-status=non-blocker
- thunderbolt/remove certification-status=non-blocker
- thunderbolt3/insert certification-status=blocker
- thunderbolt3/storage-test certification-status=blocker
- thunderbolt3/remove certification-status=blocker
+ thunderbolt/insert certification-status=blocker
+ thunderbolt/storage-test certification-status=blocker
+ thunderbolt/remove certification-status=blocker
+ thunderbolt3/insert certification-status=non-blocker
+ thunderbolt3/storage-test certification-status=non-blocker
+ thunderbolt3/remove certification-status=non-blocker
id: thunderbolt-cert-automated
unit: test plan
@@ -34,6 +43,30 @@ unit: test plan
_name: Thunderbolt tests (certification blockers only)
_description: Thunderbolt tests (certification blockers only)
include:
- thunderbolt3/insert certification-status=blocker
- thunderbolt3/storage-test certification-status=blocker
- thunderbolt3/remove certification-status=blocker
+ thunderbolt/insert certification-status=blocker
+ thunderbolt/storage-test certification-status=blocker
+ thunderbolt/remove certification-status=blocker
+
+id: after-suspend-thunderbolt-cert-manual
+unit: test plan
+_name: Thunderbolt tests (after suspend Manual)
+_description:
+ Thunderbolt tests (after suspend Manual)
+include:
+ after-suspend-manual-thunderbolt/insert certification-status=blocker
+ after-suspend-manual-thunderbolt/storage-test certification-status=blocker
+ after-suspend-manual-thunderbolt/remove certification-status=blocker
+ after-suspend-manual-thunderbolt3/insert certification-status=non-blocker
+ after-suspend-manual-thunderbolt3/storage-test certification-status=non-blocker
+ after-suspend-manual-thunderbolt3/remove certification-status=non-blocker
+
+id: after-suspend-thunderbolt-cert-blockers
+unit: test plan
+_name: Thunderbolt tests (after suspend - certification blockers only)
+_description: Thunderbolt tests (after suspend - certification blockers only)
+include:
+ after-suspend-manual-thunderbolt/insert certification-status=blocker
+ after-suspend-manual-thunderbolt/storage-test certification-status=blocker
+ after-suspend-manual-thunderbolt/remove certification-status=blocker
+
+
diff --git a/units/touchpad/test-plan.pxu b/units/touchpad/test-plan.pxu
index 20da6c5..188e7f2 100644
--- a/units/touchpad/test-plan.pxu
+++ b/units/touchpad/test-plan.pxu
@@ -17,15 +17,11 @@ include:
touchpad/basic certification-status=blocker
touchpad/palm-rejection certification-status=non-blocker
touchpad/continuous-move certification-status=blocker
- touchpad/horizontal certification-status=blocker
- touchpad/vertical certification-status=blocker
touchpad/singletouch-selection certification-status=blocker
touchpad/drag-and-drop certification-status=blocker
- touchpad/multitouch-manual certification-status=blocker
touchpad/multitouch-rightclick certification-status=blocker
touchpad/multitouch-horizontal certification-status=blocker
touchpad/multitouch-vertical certification-status=blocker
- touchpad/multitouch-dash certification-status=non-blocker
id: touchpad-cert-automated
unit: test plan
@@ -45,15 +41,11 @@ include:
touchpad/detected-as-mouse-after-suspend certification-status=blocker
touchpad/palm-rejection-after-suspend certification-status=non-blocker
touchpad/continuous-move-after-suspend certification-status=blocker
- touchpad/horizontal-after-suspend certification-status=blocker
- touchpad/vertical-after-suspend certification-status=blocker
touchpad/singletouch-selection-after-suspend certification-status=blocker
touchpad/drag-and-drop-after-suspend certification-status=blocker
- touchpad/multitouch-manual-after-suspend certification-status=blocker
touchpad/multitouch-rightclick-after-suspend certification-status=blocker
touchpad/multitouch-horizontal-after-suspend certification-status=blocker
touchpad/multitouch-vertical-after-suspend certification-status=blocker
- touchpad/multitouch-dash-after-suspend certification-status=non-blocker
id: touchpad-cert-blockers
unit: test plan
@@ -63,11 +55,8 @@ include:
touchpad/basic certification-status=blocker
touchpad/detected-as-mouse certification-status=blocker
touchpad/continuous-move certification-status=blocker
- touchpad/horizontal certification-status=blocker
- touchpad/vertical certification-status=blocker
touchpad/singletouch-selection certification-status=blocker
touchpad/drag-and-drop certification-status=blocker
- touchpad/multitouch-manual certification-status=blocker
touchpad/multitouch-rightclick certification-status=blocker
touchpad/multitouch-horizontal certification-status=blocker
touchpad/multitouch-vertical certification-status=blocker
@@ -80,11 +69,8 @@ include:
touchpad/basic-after-suspend certification-status=blocker
touchpad/detected-as-mouse-after-suspend certification-status=blocker
touchpad/continuous-move-after-suspend certification-status=blocker
- touchpad/horizontal-after-suspend certification-status=blocker
- touchpad/vertical-after-suspend certification-status=blocker
touchpad/singletouch-selection-after-suspend certification-status=blocker
touchpad/drag-and-drop-after-suspend certification-status=blocker
- touchpad/multitouch-manual-after-suspend certification-status=blocker
touchpad/multitouch-rightclick-after-suspend certification-status=blocker
touchpad/multitouch-horizontal-after-suspend certification-status=blocker
touchpad/multitouch-vertical-after-suspend certification-status=blocker
diff --git a/units/touchscreen/jobs.pxu b/units/touchscreen/jobs.pxu
index 225c168..de55c5d 100644
--- a/units/touchscreen/jobs.pxu
+++ b/units/touchscreen/jobs.pxu
@@ -59,6 +59,7 @@ _description:
2. Drag and drop the object in a different location
VERIFICATION:
Does drag and drop work?
+flags: also-after-suspend-manual
plugin: user-interact-verify
category_id: com.canonical.plainbox::touchscreen
@@ -76,6 +77,7 @@ _description:
2. Using 2 fingers, resize the blue square until it turns green, then release it.
VERIFICATION:
Did the blue square change size following the gesture?
+flags: also-after-suspend-manual
plugin: user-interact-verify
category_id: com.canonical.plainbox::touchscreen
@@ -93,6 +95,7 @@ _description:
2. Using 2 fingers, rotate the blue square until it turns green, then release it.
VERIFICATION:
Did the blue square rotate following the gesture?
+flags: also-after-suspend-manual
plugin: manual
category_id: com.canonical.plainbox::touchscreen
@@ -158,6 +161,7 @@ command:
EXIT=$?
{% endif %}
exit $EXIT
+flags: also-after-suspend-manual
plugin: user-interact-verify
template-engine: jinja2
@@ -192,3 +196,4 @@ command:
EXIT=$?
{% endif %}
exit $EXIT
+flags: also-after-suspend-manual
diff --git a/units/touchscreen/test-plan.pxu b/units/touchscreen/test-plan.pxu
index 1682136..cfe9907 100644
--- a/units/touchscreen/test-plan.pxu
+++ b/units/touchscreen/test-plan.pxu
@@ -5,8 +5,8 @@ _description:
Touchscreen tests
include:
nested_part:
- com.canonical.certification::touchscreen-cert-manual
- com.canonical.certification::touchscreen-cert-automated
+ touchscreen-cert-manual
+ touchscreen-cert-automated
id: touchscreen-cert-manual
unit: test plan
@@ -19,7 +19,6 @@ include:
touchscreen/multitouch-rotate
touchscreen/3-touch-tap certification-status=blocker
touchscreen/4-touch-tap certification-status=blocker
- touchscreen/multitouch-dash certification-status=non-blocker
id: touchscreen-cert-automated
unit: test plan
@@ -28,6 +27,34 @@ _description:
Touchscreen tests (Automated)
include:
+id: after-suspend-touchscreen-cert-full
+unit: test plan
+_name: Touchscreen tests
+_description:
+ Touchscreen tests
+include:
+nested_part:
+ after-suspend-touchscreen-cert-manual
+ after-suspend-touchscreen-cert-automated
+
+id: after-suspend-touchscreen-cert-manual
+unit: test plan
+_name: Touchscreen tests (Manual)
+_description:
+ Touchscreen tests (Manual)
+include:
+ after-suspend-manual-touchscreen/drag-n-drop certification-status=blocker
+ after-suspend-manual-touchscreen/multitouch-zoom certification-status=blocker
+ after-suspend-manual-touchscreen/multitouch-rotate
+ after-suspend-manual-touchscreen/3-touch-tap certification-status=blocker
+ after-suspend-manual-touchscreen/4-touch-tap certification-status=blocker
+
+id: after-suspend-touchscreen-cert-automated
+unit: test plan
+_name: Touchscreen tests (Automated)
+_description:
+ Touchscreen tests (Automated)
+include:
id: touchscreen-cert-blockers
unit: test plan
@@ -38,3 +65,13 @@ include:
touchscreen/multitouch-zoom certification-status=blocker
touchscreen/3-touch-tap certification-status=blocker
touchscreen/4-touch-tap certification-status=blocker
+
+id: after-suspend-touchscreen-cert-blockers
+unit: test plan
+_name: Touchscreen tests (after suspend, certification blockers only)
+_description: Touchscreen tests (after suspend, certification blockers only)
+include:
+ after-suspend-manual-touchscreen/drag-n-drop certification-status=blocker
+ 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
diff --git a/units/usb/test-plan.pxu b/units/usb/test-plan.pxu
index 63c62b8..e6a9165 100644
--- a/units/usb/test-plan.pxu
+++ b/units/usb/test-plan.pxu
@@ -102,6 +102,7 @@ include:
after-suspend-manual-usb-c/insert certification-status=blocker
after-suspend-manual-usb-c/storage-automated certification-status=blocker
after-suspend-manual-usb-c/remove certification-status=blocker
+ after-suspend-manual-usb-c/c-to-ethernet-adapter-insert
id: usb-cert-blockers
unit: test plan
diff --git a/units/usb/usb-c.pxu b/units/usb/usb-c.pxu
index cf7c5f6..0d709e2 100644
--- a/units/usb/usb-c.pxu
+++ b/units/usb/usb-c.pxu
@@ -189,6 +189,7 @@ estimated_duration: 30
id: usb-c/c-to-ethernet-adapter-insert
plugin: user-interact
+flags: also-after-suspend-manual
category_id: com.canonical.plainbox::usb
imports: from com.canonical.plainbox import manifest
requires: manifest.has_usb_type_c == 'True'
diff --git a/units/usb/usb.pxu b/units/usb/usb.pxu
index 99623fb..75cf3c9 100644
--- a/units/usb/usb.pxu
+++ b/units/usb/usb.pxu
@@ -7,7 +7,7 @@ estimated_duration: 1.0
command:
set -o pipefail
if [[ -v SNAP ]]; then
- lsusb.py -f $SNAP/var/lib/usbutils/usb.ids 2>/dev/null | sed 's/.*\(ID .*\)/\1/' | head -n 4 || echo "No USB devices were detected" >&2
+ checkbox-support-lsusb -f $SNAP/checkbox-runtime/var/lib/usbutils/usb.ids 2>/dev/null | sed 's/.*\(ID .*\)/\1/' | head -n 4 || echo "No USB devices were detected" >&2
else
lsusb 2>/dev/null | sort || echo "No USB devices were detected" >&2
fi
diff --git a/units/wireless/jobs.pxu b/units/wireless/jobs.pxu
index b3d6604..a997285 100644
--- a/units/wireless/jobs.pxu
+++ b/units/wireless/jobs.pxu
@@ -1,3 +1,15 @@
+
+id: wireless/detect
+category_id: com.canonical.plainbox::wireless
+plugin: shell
+flags: also-after-suspend
+estimated_duration: 2.0
+_summary: Detect if at least one Wireless LAN device is detected
+imports: from com.canonical.plainbox import manifest
+requires: manifest.has_wlan_adapter == 'True'
+command:
+ network_device_info.py detect WIRELESS
+
unit: template
template-resource: device
template-filter: device.category == 'WIRELESS' and device.interface != 'UNKNOWN'
@@ -18,6 +30,7 @@ requires:
{%- if __on_ubuntucore__ %}
connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager'
{% endif -%}
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'NetworkManager'
unit: template
template-resource: device
@@ -39,6 +52,7 @@ requires:
{%- if __on_ubuntucore__ %}
connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager'
{% endif -%}
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'NetworkManager'
unit: template
template-resource: device
@@ -60,6 +74,7 @@ requires:
{%- if __on_ubuntucore__ %}
connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager'
{% endif -%}
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'NetworkManager'
unit: template
template-resource: device
@@ -81,6 +96,7 @@ requires:
{%- if __on_ubuntucore__ %}
connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager'
{% endif -%}
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'NetworkManager'
unit: template
template-resource: device
@@ -102,6 +118,7 @@ requires:
{%- if __on_ubuntucore__ %}
connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager'
{% endif -%}
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'NetworkManager'
unit: template
template-resource: device
@@ -124,6 +141,7 @@ requires:
{%- if __on_ubuntucore__ %}
connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager'
{% endif -%}
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'NetworkManager'
unit: template
template-resource: device
@@ -146,6 +164,7 @@ requires:
{%- if __on_ubuntucore__ %}
connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager'
{% endif -%}
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'NetworkManager'
plugin: user-interact-verify
category_id: com.canonical.plainbox::wireless
diff --git a/units/wireless/manifest.pxu b/units/wireless/manifest.pxu
new file mode 100644
index 0000000..ed92bf6
--- /dev/null
+++ b/units/wireless/manifest.pxu
@@ -0,0 +1,4 @@
+unit: manifest entry
+id: has_wlan_adapter
+_name: A WLAN Adapter
+value-type: bool
diff --git a/units/wireless/wireless-connection-netplan.pxu b/units/wireless/wireless-connection-netplan.pxu
new file mode 100644
index 0000000..6811b43
--- /dev/null
+++ b/units/wireless/wireless-connection-netplan.pxu
@@ -0,0 +1,139 @@
+unit: template
+template-resource: device
+template-filter: device.category == 'WIRELESS'
+template-engine: jinja2
+template-unit: job
+id: wireless/wireless_connection_open_ac_np_{{ interface }}
+_summary:
+ Connect to unencrypted 802.11ac Wi-Fi network on {{ interface }} - netplan
+_purpose:
+ Check system can connect to insecure 802.11ac AP using netplan
+plugin: shell
+command:
+ net_driver_info $NET_DRIVER_INFO
+ wifi_client_test_netplan.py -i {{ interface }} -s $OPEN_AC_SSID -d
+user: root
+environ: LD_LIBRARY_PATH OPEN_AC_SSID NET_DRIVER_INFO
+category_id: com.canonical.plainbox::wireless
+estimated_duration: 15
+flags: preserve-locale also-after-suspend also-after-suspend-manual
+requires:
+ wireless_sta_protocol.{{ interface }}_ac == 'supported'
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'networkd'
+
+
+
+unit: template
+template-resource: device
+template-filter: device.category == 'WIRELESS'
+template-engine: jinja2
+template-unit: job
+id: wireless/wireless_connection_open_bg_np_{{ interface }}
+_summary:
+ Connect to unencrypted 802.11b/g Wi-Fi network on {{ interface }} - netplan
+_purpose:
+ Check system can connect to insecure 802.11b/g AP using netplan
+plugin: shell
+command:
+ net_driver_info $NET_DRIVER_INFO
+ wifi_client_test_netplan.py -i {{ interface }} -s $OPEN_BG_SSID -d
+user: root
+environ: LD_LIBRARY_PATH OPEN_BG_SSID NET_DRIVER_INFO
+category_id: com.canonical.plainbox::wireless
+estimated_duration: 15
+flags: preserve-locale also-after-suspend also-after-suspend-manual
+#requires:
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'networkd'
+
+
+unit: template
+template-resource: device
+template-filter: device.category == 'WIRELESS'
+template-engine: jinja2
+template-unit: job
+id: wireless/wireless_connection_open_n_np_{{ interface }}
+_summary:
+ Connect to unencrypted 802.11n Wi-Fi network on {{ interface }} - netplan
+_purpose:
+ Check system can connect to insecure 802.11n AP using netplan
+plugin: shell
+command:
+ net_driver_info $NET_DRIVER_INFO
+ wifi_client_test_netplan.py -i {{ interface }} -s $OPEN_N_SSID -d
+user: root
+environ: LD_LIBRARY_PATH OPEN_N_SSID NET_DRIVER_INFO
+category_id: com.canonical.plainbox::wireless
+estimated_duration: 15
+flags: preserve-locale also-after-suspend also-after-suspend-manual
+#requires:
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'networkd'
+
+
+unit: template
+template-resource: device
+template-filter: device.category == 'WIRELESS'
+template-engine: jinja2
+template-unit: job
+id: wireless/wireless_connection_wpa_ac_np_{{ interface }}
+_summary:
+ Connect to WPA-encrypted 802.11ac Wi-Fi network on {{ interface }} - netplan
+_purpose:
+ Check system can connect to 802.11ac AP with wpa security using netplan
+plugin: shell
+command:
+ net_driver_info $NET_DRIVER_INFO
+ wifi_client_test_netplan.py -i {{ interface }} -s $WPA_AC_SSID -k $WPA_AC_PSK -d
+user: root
+environ: LD_LIBRARY_PATH WPA_AC_SSID WPA_AC_PSK NET_DRIVER_INFO
+category_id: com.canonical.plainbox::wireless
+estimated_duration: 15
+flags: preserve-locale also-after-suspend also-after-suspend-manual
+requires:
+ wireless_sta_protocol.{{ interface }}_ac == 'supported'
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'networkd'
+
+
+unit: template
+template-resource: device
+template-filter: device.category == 'WIRELESS'
+template-engine: jinja2
+template-unit: job
+id: wireless/wireless_connection_wpa_bg_np_{{ interface }}
+_summary:
+ Connect to WPA-encrypted 802.11b/g Wi-Fi network on {{ interface }} - netplan
+_purpose:
+ Check system can connect to 802.11b/g AP with wpa security using netplan
+plugin: shell
+command:
+ net_driver_info $NET_DRIVER_INFO
+ wifi_client_test_netplan.py -i {{ interface }} -s $WPA_BG_SSID -k $WPA_BG_PSK -d
+user: root
+environ: LD_LIBRARY_PATH WPA_BG_SSID WPA_BG_PSK NET_DRIVER_INFO
+category_id: com.canonical.plainbox::wireless
+estimated_duration: 15
+flags: preserve-locale also-after-suspend also-after-suspend-manual
+#requires:
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'networkd'
+
+
+unit: template
+template-resource: device
+template-filter: device.category == 'WIRELESS'
+template-engine: jinja2
+template-unit: job
+id: wireless/wireless_connection_wpa_n_np_{{ interface }}
+_summary:
+ Connect to WPA-encrypted 802.11n Wi-Fi network on {{ interface }} - netplan
+_purpose:
+ Check system can connect to 802.11n AP with wpa security using netplan
+plugin: shell
+command:
+ net_driver_info $NET_DRIVER_INFO
+ wifi_client_test_netplan.py -i {{ interface }} -s $WPA_N_SSID -k $WPA_N_PSK -d
+user: root
+environ: LD_LIBRARY_PATH WPA_N_SSID WPA_N_PSK NET_DRIVER_INFO
+category_id: com.canonical.plainbox::wireless
+estimated_duration: 15
+flags: preserve-locale also-after-suspend also-after-suspend-manual
+#requires:
+# net_if_management.device == '{{ interface }}' and net_if_management.managed_by == 'networkd'