diff options
author | Sylvain Pineau <sylvain.pineau@canonical.com> | 2019-08-20 15:35:58 +0200 |
---|---|---|
committer | Sylvain Pineau <sylvain.pineau@canonical.com> | 2019-08-20 15:35:58 +0200 |
commit | f70fc8e73824ddcefd3f5de171e87a5a6e1b6b50 (patch) | |
tree | bc59f2f813741aeba4ce041375ee63f238b4ae26 | |
parent | 19561feae49044a9de817684b1d2dadea557675e (diff) |
Import plainbox-provider-checkbox_0.49.0~rc1.orig.tar.gzupstream-0.49.0_rc1patched-0.49.0_rc1-1
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 Binary files differnew file mode 100644 index 0000000..057bfec --- /dev/null +++ b/data/edids/1280x1024.edid diff --git a/data/edids/1920x1080.edid b/data/edids/1920x1080.edid Binary files differnew file mode 100644 index 0000000..567437d --- /dev/null +++ b/data/edids/1920x1080.edid diff --git a/data/edids/2560x1440.edid b/data/edids/2560x1440.edid Binary files differnew file mode 100644 index 0000000..0fd106d --- /dev/null +++ b/data/edids/2560x1440.edid @@ -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' |