diff options
author | Sylvain Pineau <sylvain.pineau@canonical.com> | 2018-07-23 16:31:15 +0200 |
---|---|---|
committer | Sylvain Pineau <sylvain.pineau@canonical.com> | 2018-07-23 16:31:15 +0200 |
commit | ff430da678446384effe5c71b4f6593566e4877f (patch) | |
tree | d6a549ac27789f2a5deb456c75671c9c60429711 | |
parent | 269bdb527cbcbebd7345f03e8fcc89d22b24c8f7 (diff) | |
parent | 6ca200fedf88bf8fba57b3f2eda59799d6f574cd (diff) |
record new upstream branch created by importing plainbox-provider-checkbox_0.46.0~rc1.orig.tar.gz and merge it
59 files changed, 1868 insertions, 810 deletions
diff --git a/bin/accelerometer_test b/bin/accelerometer_test index 93130fc..2d95ca8 100755 --- a/bin/accelerometer_test +++ b/bin/accelerometer_test @@ -24,13 +24,17 @@ accelerometer, and check to be sure that the x, y, z axis respond to physical movement of hardware. ''' from argparse import ArgumentParser -from gi.repository import Gdk, GLib, Gtk +import gi import logging import os import re import sys import threading import time +gi.require_version('Gdk', '3.0') +gi.require_version('GLib', '2.0') +gi.require_version("Gtk", "3.0") +from gi.repository import Gdk, GLib, Gtk from subprocess import Popen, PIPE, check_output, STDOUT, CalledProcessError from checkbox_support.parsers.modinfo import ModinfoParser diff --git a/bin/audio_test b/bin/audio_test index f0b73a4..466b760 100755 --- a/bin/audio_test +++ b/bin/audio_test @@ -12,6 +12,7 @@ import sys import time try: import gi + gi.require_version('GLib', '2.0') gi.require_version('Gst','1.0') from gi.repository import GObject from gi.repository import Gst diff --git a/bin/battery_test b/bin/battery_test index cdfd227..b2682c7 100755 --- a/bin/battery_test +++ b/bin/battery_test @@ -1,11 +1,13 @@ #!/usr/bin/env python3 +import gi import os import time import re import subprocess import sys import argparse +gi.require_version('Gio', '2.0') from gi.repository import Gio diff --git a/bin/boot_mode_test_snappy.py b/bin/boot_mode_test_snappy.py new file mode 100755 index 0000000..46bed92 --- /dev/null +++ b/bin/boot_mode_test_snappy.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# Copyright 2018 Canonical Ltd. +# Written by: +# Jonathan Cave <jonathan.cave@canonical.com> + +import io +import os +import re +import sys +import subprocess as sp + +import yaml + + +def fitdumpimage(filename): + cmd = 'dumpimage -l {}'.format(filename) + out = sp.check_output(cmd, shell=True).decode(sys.stdout.encoding) + buf = io.StringIO(out) + + # first line should identify FIT file + if not buf.readline().startswith('FIT description'): + raise SystemExit('ERROR: expected FIT image description') + + # second line contains some metadata, skip it + buf.readline() + + # from then on should get blocks of text describing the objects that were + # combined in to the FIT image e.g. kernel, ramdisk, device tree + image_re = re.compile(r'(?:^\ Image)\ \d+\ \((\S+)\)$') + config_re = re.compile(r'^\ Default Configuration|^\ Configuration') + objects = {} + name = '' + while True: + line = buf.readline() + # stop at end + if line == '': + break + # interested in storing image information + match = image_re.search(line) + if match: + name = match.group(1) + objects[name] = {} + continue + # not interested in configurations + if config_re.search(line): + name = '' + continue + # while in an image section store the info + if name != '': + entries = [s.strip() for s in line.split(':', 1)] + objects[name][entries[0]] = entries[1] + return objects + + +def main(): + if len(sys.argv) != 3: + raise SystemExit('ERROR: please supply gadget & kernel name') + gadget = sys.argv[1] + kernel = sys.argv[2] + + gadget_yaml = os.path.join('/snap', gadget, 'current/meta/gadget.yaml') + + if not os.path.exists(gadget_yaml): + raise SystemExit( + 'ERROR: failed to find gadget.yaml at {}'.format(gadget_yaml)) + + with open(gadget_yaml) as f: + data = yaml.load(f) + for k in data['volumes'].keys(): + bootloader = data['volumes'][k]['bootloader'] + if not bootloader: + raise SystemExit('ERROR: could not find name of bootloader') + + if bootloader not in ('u-boot', 'grub'): + raise SystemExit( + 'ERROR: Unexpected bootloader name {}'.format(bootloader)) + print('Bootloader is {}\n'.format(bootloader)) + + if bootloader == 'u-boot': + print('Parsing FIT image information...\n') + + kernel_rev = os.path.basename( + os.path.realpath('/snap/{}/current'.format(kernel))) + boot_kernel = '/boot/uboot/{}_{}.snap/kernel.img'.format( + kernel, kernel_rev) + boot_objects = fitdumpimage(boot_kernel) + + for obj, attrs in boot_objects.items(): + print('Checking object {}'.format(obj)) + if 'Sign value' not in attrs: + raise SystemExit('ERROR: no sign value found for object') + print('Found "Sign value"') + if len(attrs['Sign value']) != 512: + raise SystemExit('ERROR: unexpected sign value size') + if all(s in attrs['Sign algo'] for s in ['sha256', 'rsa2048']): + print('Found expected signing algorithms') + else: + raise SystemExit( + 'ERROR: unexpected signing algorithms {}'.format( + attrs['Sign algo'])) + print() + + # check that all parts of the fit image have + snap_kernel = '/snap/{}/current/kernel.img'.format(kernel) + snap_objects = fitdumpimage(snap_kernel) + if snap_objects != boot_objects: + raise SystemExit( + 'ERROR: boot kernel and current snap kernel do not match') + print('Kernel images in current snap and u-boot match\n') + + print('Secure Boot appears to be enabled on this system') + + if bootloader == 'grub': + cmd = 'mokutil --sb-state' + print('+', cmd, flush=True) + out = sp.check_output(cmd, shell=True).decode(sys.stdout.encoding) + print(out, flush=True) + if out != 'SecureBoot enabled\n': + raise SystemExit('ERROR: mokutil reports Secure Boot not in use') + + print('Secure Boot appears to be enabled on this system') + + +if __name__ == '__main__': + main() diff --git a/bin/camera_test_legacy b/bin/camera_test_legacy deleted file mode 100755 index 794578b..0000000 --- a/bin/camera_test_legacy +++ /dev/null @@ -1,578 +0,0 @@ -#!/usr/bin/env python3 -# -# This file is part of Checkbox. -# -# Copyright 2008-2012 Canonical Ltd. -# -# The v4l2 ioctl code comes from the Python bindings for the v4l2 -# userspace api (http://pypi.python.org/pypi/v4l2): -# Copyright (C) 1999-2009 the contributors -# -# The JPEG metadata parser is a part of bfg-pages: -# http://code.google.com/p/bfg-pages/source/browse/trunk/pages/getimageinfo.py -# Copyright (C) Tim Hoffman -# -# Checkbox is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3, -# as published by the Free Software Foundation. - -# -# Checkbox is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. -# - -import argparse -import ctypes -import errno -import fcntl -import imghdr -import logging -import os -import re -import struct -import sys -import time - -from gi.repository import GObject -from glob import glob -from subprocess import check_call, CalledProcessError, STDOUT -from tempfile import NamedTemporaryFile - - -_IOC_NRBITS = 8 -_IOC_TYPEBITS = 8 -_IOC_SIZEBITS = 14 - -_IOC_NRSHIFT = 0 -_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS -_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS -_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS - -_IOC_WRITE = 1 -_IOC_READ = 2 - - -def _IOC(dir_, type_, nr, size): - return ( - ctypes.c_int32(dir_ << _IOC_DIRSHIFT).value | - ctypes.c_int32(ord(type_) << _IOC_TYPESHIFT).value | - ctypes.c_int32(nr << _IOC_NRSHIFT).value | - ctypes.c_int32(size << _IOC_SIZESHIFT).value) - - -def _IOC_TYPECHECK(t): - return ctypes.sizeof(t) - - -def _IOR(type_, nr, size): - return _IOC(_IOC_READ, type_, nr, ctypes.sizeof(size)) - - -def _IOWR(type_, nr, size): - return _IOC(_IOC_READ | _IOC_WRITE, type_, nr, _IOC_TYPECHECK(size)) - - -class v4l2_capability(ctypes.Structure): - """ - Driver capabilities - """ - _fields_ = [ - ('driver', ctypes.c_char * 16), - ('card', ctypes.c_char * 32), - ('bus_info', ctypes.c_char * 32), - ('version', ctypes.c_uint32), - ('capabilities', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - - -# Values for 'capabilities' field -V4L2_CAP_VIDEO_CAPTURE = 0x00000001 -V4L2_CAP_VIDEO_OVERLAY = 0x00000004 -V4L2_CAP_READWRITE = 0x01000000 -V4L2_CAP_STREAMING = 0x04000000 - -v4l2_frmsizetypes = ctypes.c_uint -( - V4L2_FRMSIZE_TYPE_DISCRETE, - V4L2_FRMSIZE_TYPE_CONTINUOUS, - V4L2_FRMSIZE_TYPE_STEPWISE, -) = range(1, 4) - - -class v4l2_frmsize_discrete(ctypes.Structure): - _fields_ = [ - ('width', ctypes.c_uint32), - ('height', ctypes.c_uint32), - ] - - -class v4l2_frmsize_stepwise(ctypes.Structure): - _fields_ = [ - ('min_width', ctypes.c_uint32), - ('min_height', ctypes.c_uint32), - ('step_width', ctypes.c_uint32), - ('min_height', ctypes.c_uint32), - ('max_height', ctypes.c_uint32), - ('step_height', ctypes.c_uint32), - ] - - -class v4l2_frmsizeenum(ctypes.Structure): - class _u(ctypes.Union): - _fields_ = [ - ('discrete', v4l2_frmsize_discrete), - ('stepwise', v4l2_frmsize_stepwise), - ] - - _fields_ = [ - ('index', ctypes.c_uint32), - ('pixel_format', ctypes.c_uint32), - ('type', ctypes.c_uint32), - ('_u', _u), - ('reserved', ctypes.c_uint32 * 2) - ] - - _anonymous_ = ('_u',) - - -class v4l2_fmtdesc(ctypes.Structure): - _fields_ = [ - ('index', ctypes.c_uint32), - ('type', ctypes.c_int), - ('flags', ctypes.c_uint32), - ('description', ctypes.c_char * 32), - ('pixelformat', ctypes.c_uint32), - ('reserved', ctypes.c_uint32 * 4), - ] - -V4L2_FMT_FLAG_COMPRESSED = 0x0001 -V4L2_FMT_FLAG_EMULATED = 0x0002 - -# ioctl code for video devices -VIDIOC_QUERYCAP = _IOR('V', 0, v4l2_capability) -VIDIOC_ENUM_FRAMESIZES = _IOWR('V', 74, v4l2_frmsizeenum) -VIDIOC_ENUM_FMT = _IOWR('V', 2, v4l2_fmtdesc) - - -class CameraTest: - """ - A simple class that displays a test image via GStreamer. - """ - def __init__(self, args, gst_plugin=None, gst_video_type=None): - self.args = args - self._mainloop = GObject.MainLoop() - self._width = 640 - self._height = 480 - self._gst_plugin = gst_plugin - self._gst_video_type = gst_video_type - - def detect(self): - """ - Display information regarding webcam hardware - """ - cap_status = dev_status = 1 - for i in range(10): - cp = v4l2_capability() - device = '/dev/video%d' % i - try: - with open(device, 'r') as vd: - fcntl.ioctl(vd, VIDIOC_QUERYCAP, cp) - except IOError: - continue - dev_status = 0 - print("%s: OK" % device) - print(" name : %s" % cp.card.decode('UTF-8')) - print(" driver : %s" % cp.driver.decode('UTF-8')) - print(" version: %s.%s.%s" - % (cp.version >> 16, - (cp.version >> 8) & 0xff, - cp.version & 0xff)) - print(" flags : 0x%x [" % cp.capabilities, - ' CAPTURE' if cp.capabilities & V4L2_CAP_VIDEO_CAPTURE - else '', - ' OVERLAY' if cp.capabilities & V4L2_CAP_VIDEO_OVERLAY - else '', - ' READWRITE' if cp.capabilities & V4L2_CAP_READWRITE - else '', - ' STREAMING' if cp.capabilities & V4L2_CAP_STREAMING - else '', - ' ]', sep="") - - resolutions = self._get_supported_resolutions(device) - print(' ', - self._supported_resolutions_to_string(resolutions).replace( - "\n", " "), - sep="") - - if cp.capabilities & V4L2_CAP_VIDEO_CAPTURE: - cap_status = 0 - return dev_status | cap_status - - def led(self): - """ - Activate camera (switch on led), but don't display any output - """ - pipespec = ("v4l2src device=%(device)s " - "! %(type)s " - "! %(plugin)s " - "! testsink" - % {'device': self.args.device, - 'type': self._gst_video_type, - 'plugin': self._gst_plugin}) - logging.debug("LED test with pipeline %s", pipespec) - self._pipeline = Gst.parse_launch(pipespec) - self._pipeline.set_state(Gst.State.PLAYING) - time.sleep(3) - self._pipeline.set_state(Gst.State.NULL) - - def display(self): - """ - Displays the preview window - """ - pipespec = ("v4l2src device=%(device)s " - "! %(type)s,width=%(width)d,height=%(height)d " - "! %(plugin)s " - "! autovideosink" - % {'device': self.args.device, - 'type': self._gst_video_type, - 'width': self._width, - 'height': self._height, - 'plugin': self._gst_plugin}) - logging.debug("display test with pipeline %s", pipespec) - self._pipeline = Gst.parse_launch(pipespec) - self._pipeline.set_state(Gst.State.PLAYING) - time.sleep(10) - self._pipeline.set_state(Gst.State.NULL) - - def still(self): - """ - Captures an image to a file - """ - if self.args.filename: - self._still_helper(self.args.filename, self._width, self._height, - self.args.quiet) - else: - with NamedTemporaryFile(prefix='camera_test_', suffix='.jpg') as f: - self._still_helper(f.name, self._width, self._height, - self.args.quiet) - - def _still_helper(self, filename, width, height, quiet, pixelformat=None): - """ - Captures an image to a given filename. width and height specify the - image size and quiet controls whether the image is displayed to the - user (quiet = True means do not display image). - """ - command = ["fswebcam", "-D 1", "-S 50", "--no-banner", - "-d", self.args.device, - "-r", "%dx%d" - % (width, height), filename] - use_gstreamer = False - if pixelformat: - if 'MJPG' == pixelformat: # special tweak for fswebcam - pixelformat = 'MJPEG' - command.extend(["-p", pixelformat]) - - try: - check_call(command, stdout=open(os.devnull, 'w'), stderr=STDOUT) - except (CalledProcessError, OSError): - use_gstreamer = True - - if use_gstreamer: - pipespec = ("v4l2src device=%(device)s " - "! %(type)s,width=%(width)d,height=%(height)d " - "! %(plugin)s " - "! jpegenc " - "! filesink location=%(filename)s" - % {'device': self.args.device, - 'type': self._gst_video_type, - 'width': width, - 'height': height, - 'plugin': self._gst_plugin, - 'filename': filename}) - logging.debug("still test with gstreamer and " - "pipeline %s", pipespec) - self._pipeline = Gst.parse_launch(pipespec) - self._pipeline.set_state(Gst.State.PLAYING) - time.sleep(3) - self._pipeline.set_state(Gst.State.NULL) - - if not quiet: - import imghdr - image_type = imghdr.what(filename) - pipespec = ("filesrc location=%(filename)s ! " - "%(type)sdec ! " - "videoscale ! " - "imagefreeze ! autovideosink" - % {'filename': filename, - 'type': image_type}) - self._pipeline = Gst.parse_launch(pipespec) - self._pipeline.set_state(Gst.State.PLAYING) - time.sleep(10) - self._pipeline.set_state(Gst.State.NULL) - - def _supported_resolutions_to_string(self, supported_resolutions): - """ - Return a printable string representing a list of supported resolutions - """ - ret = "" - for resolution in supported_resolutions: - ret += "Format: %s (%s)\n" % (resolution['pixelformat'], - resolution['description']) - ret += "Resolutions: " - for res in resolution['resolutions']: - ret += "%sx%s," % (res[0], res[1]) - # truncate the extra comma with :-1 - ret = ret[:-1] + "\n" - return ret - - def resolutions(self): - """ - After querying the webcam for supported formats and resolutions, - take multiple images using the first format returned by the driver, - and see if they are valid - """ - resolutions = self._get_supported_resolutions(self.args.device) - # print supported formats and resolutions for the logs - print(self._supported_resolutions_to_string(resolutions)) - - # pick the first format, which seems to be what the driver wants for a - # default. This also matches the logic that fswebcam uses to select - # a default format. - resolution = resolutions[0] - if resolution: - print("Taking multiple images using the %s format" - % resolution['pixelformat']) - for res in resolution['resolutions']: - w = res[0] - h = res[1] - f = NamedTemporaryFile(prefix='camera_test_%s%sx%s' % - (resolution['pixelformat'], w, h), - suffix='.jpg', delete=False) - print("Taking a picture at %sx%s" % (w, h)) - self._still_helper(f.name, w, h, True, - pixelformat=resolution['pixelformat']) - if self._validate_image(f.name, w, h): - print("Validated image %s" % f.name) - os.remove(f.name) - else: - print("Failed to validate image %s" % f.name, - file=sys.stderr) - os.remove(f.name) - return 1 - return 0 - - def _get_pixel_formats(self, device, maxformats=5): - """ - Query the camera to see what pixel formats it supports. A list of - dicts is returned consisting of format and description. The caller - should check whether this camera supports VIDEO_CAPTURE before - calling this function. - """ - supported_formats = [] - fmt = v4l2_fmtdesc() - fmt.index = 0 - fmt.type = V4L2_CAP_VIDEO_CAPTURE - try: - while fmt.index < maxformats: - with open(device, 'r') as vd: - if fcntl.ioctl(vd, VIDIOC_ENUM_FMT, fmt) == 0: - pixelformat = {} - # save the int type for re-use later - pixelformat['pixelformat_int'] = fmt.pixelformat - pixelformat['pixelformat'] = "%s%s%s%s" % \ - (chr(fmt.pixelformat & 0xFF), - chr((fmt.pixelformat >> 8) & 0xFF), - chr((fmt.pixelformat >> 16) & 0xFF), - chr((fmt.pixelformat >> 24) & 0xFF)) - pixelformat['description'] = fmt.description.decode() - supported_formats.append(pixelformat) - fmt.index = fmt.index + 1 - except IOError as e: - # EINVAL is the ioctl's way of telling us that there are no - # more formats, so we ignore it - if e.errno != errno.EINVAL: - print("Unable to determine Pixel Formats, this may be a " - "driver issue.") - return supported_formats - return supported_formats - - def _get_supported_resolutions(self, device): - """ - Query the camera for supported resolutions for a given pixel_format. - Data is returned in a list of dictionaries with supported pixel - formats as the following example shows: - resolution['pixelformat'] = "YUYV" - resolution['description'] = "(YUV 4:2:2 (YUYV))" - resolution['resolutions'] = [[width, height], [640, 480], [1280, 720] ] - - If we are unable to gather any information from the driver, then we - return YUYV and 640x480 which seems to be a safe default. - Per the v4l2 spec the ioctl used here is experimental - but seems to be well supported. - """ - supported_formats = self._get_pixel_formats(device) - if not supported_formats: - resolution = {} - resolution['description'] = "YUYV" - resolution['pixelformat'] = "YUYV" - resolution['resolutions'] = [[640, 480]] - supported_formats.append(resolution) - return supported_formats - - for supported_format in supported_formats: - resolutions = [] - framesize = v4l2_frmsizeenum() - framesize.index = 0 - framesize.pixel_format = supported_format['pixelformat_int'] - with open(device, 'r') as vd: - try: - while fcntl.ioctl(vd, - VIDIOC_ENUM_FRAMESIZES, - framesize) == 0: - if framesize.type == V4L2_FRMSIZE_TYPE_DISCRETE: - resolutions.append([framesize.discrete.width, - framesize.discrete.height]) - # for continuous and stepwise, let's just use min and - # max they use the same structure and only return - # one result - elif (framesize.type in (V4L2_FRMSIZE_TYPE_CONTINUOUS, - V4L2_FRMSIZE_TYPE_STEPWISE)): - resolutions.append([framesize.stepwise.min_width, - framesize.stepwise.min_height] - ) - resolutions.append([framesize.stepwise.max_width, - framesize.stepwise.max_height] - ) - break - framesize.index = framesize.index + 1 - except IOError as e: - # EINVAL is the ioctl's way of telling us that there are no - # more formats, so we ignore it - if e.errno != errno.EINVAL: - print("Unable to determine supported framesizes " - "(resolutions), this may be a driver issue.") - supported_format['resolutions'] = resolutions - return supported_formats - - def _validate_image(self, filename, width, height): - """ - Given a filename, ensure that the image is the width and height - specified and is a valid image file. - """ - if imghdr.what(filename) != 'jpeg': - return False - - outw = outh = 0 - with open(filename, mode='rb') as jpeg: - jpeg.seek(2) - b = jpeg.read(1) - try: - while (b and ord(b) != 0xDA): - while (ord(b) != 0xFF): - b = jpeg.read(1) - while (ord(b) == 0xFF): - b = jpeg.read(1) - if (ord(b) >= 0xC0 and ord(b) <= 0xC3): - jpeg.seek(3, 1) - h, w = struct.unpack(">HH", jpeg.read(4)) - break - b = jpeg.read(1) - outw, outh = int(w), int(h) - except (struct.error, ValueError): - pass - - if outw != width: - print("Image width does not match, was %s should be %s" % - (outw, width), file=sys.stderr) - return False - if outh != height: - print("Image width does not match, was %s should be %s" % - (outh, height), file=sys.stderr) - return False - - return True - - return True - - -def parse_arguments(argv): - """ - Parse command line arguments - """ - parser = argparse.ArgumentParser(description="Run a camera-related test") - subparsers = parser.add_subparsers(dest='test', - title='test', - description='Available camera tests') - - parser.add_argument('--debug', dest='log_level', - action="store_const", const=logging.DEBUG, - default=logging.INFO, help="Show debugging messages") - - def add_device_parameter(parser): - group = parser.add_mutually_exclusive_group() - group.add_argument("-d", "--device", default="/dev/video0", - help="Device for the webcam to use") - group.add_argument("--highest-device", action="store_true", - help=("Use the /dev/videoN " - "where N is the highest value available")) - group.add_argument("--lowest-device", action="store_true", - help=("Use the /dev/videoN " - "where N is the lowest value available")) - subparsers.add_parser('detect') - led_parser = subparsers.add_parser('led') - add_device_parameter(led_parser) - display_parser = subparsers.add_parser('display') - add_device_parameter(display_parser) - still_parser = subparsers.add_parser('still') - add_device_parameter(still_parser) - still_parser.add_argument("-f", "--filename", - help="Filename to store the picture") - still_parser.add_argument("-q", "--quiet", action="store_true", - help=("Don't display picture, " - "just write the picture to a file")) - resolutions_parser = subparsers.add_parser('resolutions') - add_device_parameter(resolutions_parser) - args = parser.parse_args(argv) - - def get_video_devices(): - devices = sorted(glob('/dev/video[0-9]'), - key=lambda d: re.search(r'\d', d).group(0)) - assert len(devices) > 0, "No video devices found" - return devices - - if hasattr(args, 'highest_device') and args.highest_device: - args.device = get_video_devices()[-1] - elif hasattr(args, 'lowest_device') and args.lowest_device: - args.device = get_video_devices()[0] - return args - - -if __name__ == "__main__": - args = parse_arguments(sys.argv[1:]) - - if not args.test: - args.test = 'detect' - - logging.basicConfig(level=args.log_level) - - # Import Gst only for the test cases that will need it - if args.test in ['display', 'still', 'led', 'resolutions']: - from gi.repository import Gst - if Gst.version()[0] > 0: - gst_plugin = 'videoconvert' - gst_video_type = 'video/x-raw' - else: - gst_plugin = 'ffmpegcolorspace' - gst_video_type = 'video/x-raw-yuv' - Gst.init(None) - camera = CameraTest(args, gst_plugin, gst_video_type) - else: - camera = CameraTest(args) - - sys.exit(getattr(camera, args.test)()) diff --git a/bin/check-prerelease b/bin/check-prerelease new file mode 100755 index 0000000..1f3d017 --- /dev/null +++ b/bin/check-prerelease @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +""" +Script to test that the system is NOT running prerelease software + +Copyright (c) 2018 Canonical Ltd. + +Authors + Rod Smith <rod.smith@canonical.com> + +This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>. + +The purpose of this script is to identify whether an EFI-based +system booted from the network (test passes) or from a local disk +(test fails). + +Usage: + check-prerelease +""" + +import platform +import shlex +import sys + +from subprocess import Popen, PIPE + + +def check_kernel_status(): + """Check kernel to see if it's supported for certification + + :returns: + True if OK, False if not + """ + kernel_release = platform.release() + + retval = False + command = "apt-cache show linux-image-{}".format(kernel_release) + aptinfo = [] + aptinfo_bytes = (Popen(shlex.split(command), stdout=PIPE) + .communicate()[0]) + aptinfo = (aptinfo_bytes.decode(encoding="utf-8", errors="ignore") + .splitlines()) + + """Kernel apt-cache info includes a 'Supported:' line on release + kernels to identify the period of support. This line is missing on + pre-release kernels. Thus, we return a True value only if this + line is present and shows a 5-year support period.""" + if any("Supported: 5y" in s for s in aptinfo): + retval = True + if any("Supported: 9m" in s for s in aptinfo): + print("* Kernel is supported for 9 months; it is an interim release!") + if not any("Supported:" in s for s in aptinfo): + print("* Could not find a kernel support period; " + "may be a pre-release kernel!") + + """We also want to exclude 'edge' kernels, which are identified via + the 'Source:' line in the apt-cache output....""" + if any("Source: linux-signed-hwe-edge" in s for s in aptinfo): + print("* Kernel is an 'edge' kernel!") + retval = False + if any("Source: linux-hwe-edge" in s for s in aptinfo): + print("* Kernel is an 'edge' kernel!") + retval = False + + """We also want to exclude low-latency kernels, which are identified + via the kernel name string itself....""" + if "lowlatency" in kernel_release: + print("* Kernel is a low-latency kernel!") + retval = False + + if (not retval): + print("* Kernel release is {}".format(kernel_release)) + print("* Kernel is ineligible for certification!") + + return retval + + +def check_os_status(): + """Check OS to see if it's supported for certification. Note that this + passes any release version (even a non-LTS version), but not pre-release + versions. + + :returns: + True if OK, False if not + """ + retval = True + command = "lsb_release -s -d" + lsbinfo = [] + lsbinfo_bytes = (Popen(shlex.split(command), stdout=PIPE) + .communicate()[0]) + lsbinfo = (lsbinfo_bytes.decode(encoding="utf-8", errors="ignore") + .rstrip()) + + """OS information include '(development branch)' on pre-release + installations. Such installations should fail this test.""" + if "(development branch)" in lsbinfo: + print("* 'lsb_release -s -d' result is '{}'".format(lsbinfo)) + print("* OS is reported as a development branch:") + print("* {}".format(lsbinfo)) + retval = False + print("") + + return retval + + +def main(): + """Check to see if the machine is running pre-release kernel or OS.""" + + retval = 0 + if (not check_kernel_status()): + retval = 1 + if (not check_os_status()): + retval += 2 + if (retval == 0): + print("** All OK; production kernel and OS.") + elif (retval == 1): + print("** Test FAILS; running ineligible kernel!") + elif (retval == 2): + print("** Test FAILS; running pre-release OS!") + else: + print("** Test FAILS; running pre-release OS with ineligible kernel!") + + return(retval) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/bin/cycle_vts b/bin/cycle_vts index 8b7f5bc..c5e1677 100755 --- a/bin/cycle_vts +++ b/bin/cycle_vts @@ -12,14 +12,14 @@ then exit 1 fi -if [ "$CURRENT_VT" -ne "1" ] +if [ "$CURRENT_VT" == "7" ] then chvt 1 else - chvt 2 + chvt 3 fi -sleep 2 +sleep 5 chvt "$CURRENT_VT" sleep 2 diff --git a/bin/disk_info b/bin/disk_info index 5efedbd..0b45443 100755 --- a/bin/disk_info +++ b/bin/disk_info @@ -53,7 +53,7 @@ def main(): disks = 0 for line in lsblk.splitlines(): m = pattern.match(line) - if not m or m.group('TYPE') != 'disk': + if not m or m.group('TYPE') not in ('disk', 'crypt'): continue # Only consider MMC block devices if one of their mounted partitions is # root (/) diff --git a/bin/disk_read_performance_test b/bin/disk_read_performance_test index e2a219d..00810a5 100755 --- a/bin/disk_read_performance_test +++ b/bin/disk_read_performance_test @@ -4,7 +4,7 @@ # #Default to a lower bound of 15 MB/s -DEFAULT_BUF_READ=15 +DEFAULT_BUF_READ=${DISK_READ_PERF:-15} for disk in $@; do diff --git a/bin/efi-pxeboot b/bin/efi-pxeboot index 724fefb..24ac231 100755 --- a/bin/efi-pxeboot +++ b/bin/efi-pxeboot @@ -49,6 +49,7 @@ def discover_data(): .splitlines()) boot_entries = {} boot_order = [] + boot_current = "" if len(bootinfo) > 1: for s in bootinfo: if "BootOrder" in s: @@ -103,7 +104,7 @@ def is_pxe_booted(boot_entries, boot_order, boot_current): or "refind_" in desc: # This string indicates a boot directly from the normal Ubuntu GRUB # or rEFInd installation on the hard disk. - print("The system seems to have booted directly from the hard disk!") + print("FAIL: The system has booted directly from the hard disk!") retval = 1 elif "SATA" in desc or "Sata" in desc or "Hard Drive" in desc: # These strings indicate booting with a "generic" disk entry (one diff --git a/bin/fde_tests.py b/bin/fde_tests.py new file mode 100755 index 0000000..3008735 --- /dev/null +++ b/bin/fde_tests.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# Copyright 2018 Canonical Ltd. +# Written by: +# Jonathan Cave <jonathan.cave@canonical.com> + +"""Test that Full Disk Encryption is in use. + +$ fde_tests.py +Canonical has a reference implementation of full disk encryption for IoT +devices. With no arguments passed this test checks this implementation is in +operation on the device under test. + +$ fde_tests.py desktop +Checks if the system appears to be using full disk encryption as configured by +the desktop installer. +""" + +import os +import re +import subprocess as sp +import sys + + +def main(): + on_desktop = len(sys.argv) > 1 and sys.argv[1] == 'desktop' + + # the mountpoint corresponding to the on disk encrypted partition + base_mount = '/' if on_desktop else '/writable' + + # discover the underlying mount point for the encrypted part + cmd = 'findmnt {} -n -o SOURCE'.format(base_mount) + print('+ {}'.format(cmd)) + try: + source = sp.check_output(cmd, shell=True).decode( + sys.stdout.encoding).strip() + except sp.CalledProcessError: + raise SystemExit( + 'ERROR: could not find mountpoint for {}'.format(base_mount)) + print(source, '\n') + + # resolve the source to an actual device node + print('+ realpath {}'.format(source)) + device = os.path.realpath(source) + print(device, '\n') + + # work upwards through the tree of devices until we find the one that has + # the type 'crypt' + kname = os.path.basename(device) + while True: + cmd = 'lsblk -r -n -i -o KNAME,TYPE,PKNAME | grep "^{}"'.format(kname) + print('+ {}'.format(cmd)) + try: + lsblk = sp.check_output(cmd, shell=True).decode( + sys.stdout.encoding).strip() + except sp.CalledProcessError: + raise SystemExit('ERROR: lsblk call failed') + _, devtype, parent = lsblk.split(maxsplit=2) + print(devtype, '\n') + if devtype == 'crypt': + # found the device + break + if devtype == 'disk': + # reached the physical device, end the search + raise SystemExit( + 'ERROR: could not find a block device of type "crypt"') + kname = parent + + # the presence of device with type 'crypt' is probably confirmation enough + # but to be really sure lets check to see it is found by cryptsetup + + # first we need to know its mapper name + cmd = 'dmsetup info /dev/{} | grep "^Name:"'.format(kname) + print('+ {}'.format(cmd)) + try: + mapper_name = sp.check_output(cmd, shell=True).decode( + sys.stdout.encoding).strip().split()[-1] + except sp.CalledProcessError: + raise SystemExit( + 'ERROR: dmsetup info on device {} failed'.format(kname)) + print(mapper_name, '\n') + + # then query the info in cryptsetup + cmd = 'cryptsetup status {}'.format(mapper_name) + print('+ {}'.format(cmd)) + try: + cryptinfo = sp.check_output(cmd, shell=True).decode( + sys.stdout.encoding).strip() + except sp.CalledProcessError: + raise SystemExit('ERROR: dmsetup failed') + print(cryptinfo, '\n') + + # use the type as the final arbiter of success + regexp = re.compile(r'type:\ *LUKS1') + if regexp.search(cryptinfo): + print('Full Disk Encryption is operational on this device') + else: + raise SystemExit('ERROR: cryptsetup did not report LUKS1 in use') + + +if __name__ == "__main__": + main() diff --git a/bin/gatt-notify-test.py b/bin/gatt-notify-test.py new file mode 100755 index 0000000..9923a93 --- /dev/null +++ b/bin/gatt-notify-test.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +# +# Copyright 2018 Canonical Ltd. +# Written by: +# Sylvain Pineau <sylvain.pineau@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/>. + +import argparse +import logging +import os +import sys +import time + +import dbus +import dbus.service +import dbus.mainloop.glib +from gi.repository import GObject + +logger = logging.getLogger(__file__) +logger.addHandler(logging.StreamHandler(sys.stdout)) + +ADAPTER_INTERFACE = 'org.bluez.Adapter1' +DEVICE_INTERFACE = 'org.bluez.Device1' +PROP_INTERFACE = 'org.freedesktop.DBus.Properties' +OM_INTERFACE = 'org.freedesktop.DBus.ObjectManager' +GATT_SERVICE_INTERFACE = 'org.bluez.GattService1' +GATT_CHRC_INTERFACE = 'org.bluez.GattCharacteristic1' + +dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + +class BtAdapter: + """Bluetooth LE Adapter class.""" + def __init__(self, pattern): + self._pattern = os.path.basename(pattern) + self._bus = dbus.SystemBus() + self._manager = dbus.Interface( + self._bus.get_object("org.bluez", "/"), OM_INTERFACE) + self._main_loop = GObject.MainLoop() + self._adapter = self._find_adapter() + self._path = self._adapter.object_path + self._props = dbus.Interface(self._adapter, PROP_INTERFACE) + self._name = self._props.Get(ADAPTER_INTERFACE, "Name") + self._addr = self._props.Get(ADAPTER_INTERFACE, "Address") + self._alias = self._props.Get(ADAPTER_INTERFACE, "Alias") + logger.info('Adapter found: [ {} ] {} - {}'.format( + self._path, self._addr, self._alias)) + + def _get_managed_objects(self): + return self._manager.GetManagedObjects() + + def _find_adapter(self): + for path, ifaces in self._get_managed_objects().items(): + adapter = ifaces.get(ADAPTER_INTERFACE) + if adapter is None: + continue + if (self._pattern == adapter["Address"] or + path.endswith(self._pattern)): + obj = self._bus.get_object("org.bluez", path) + return dbus.Interface(obj, ADAPTER_INTERFACE) + raise SystemExit("Bluetooth adapter not found!") + + def ensure_powered(self): + """Turn the adapter on.""" + self._props.Set(ADAPTER_INTERFACE, "Powered", dbus.Boolean(1)) + logger.info('Adapter powered on') + + def scan(self, timeout=10): + """Scan for BT devices.""" + dbus.Interface(self._adapter, ADAPTER_INTERFACE).StartDiscovery() + logger.info('Adapter scan on ({}s)'.format(timeout)) + GObject.timeout_add_seconds(timeout, self._scan_timeout) + self._main_loop.run() + + def _scan_timeout(self): + dbus.Interface(self._adapter, ADAPTER_INTERFACE).StopDiscovery() + logger.info('Adapter scan completed') + self._main_loop.quit() + + def find_device_with_service(self, ADV_SVC_UUID): + """Find a device with a given remote service.""" + for path, ifaces in self._get_managed_objects().items(): + device = ifaces.get(DEVICE_INTERFACE) + if device is None: + continue + logger.debug("{} {} {}".format( + path, device["Address"], device["Alias"])) + if ADV_SVC_UUID in device["UUIDs"] and path.startswith(self._path): + obj = self._bus.get_object("org.bluez", path) + logger.info('Device found: [ {} ] {} - {}'.format( + path, device["Name"], device["Address"])) + return dbus.Interface(obj, DEVICE_INTERFACE) + raise SystemExit("Bluetooth device not found!") + + def remove_device(self, device): + """Remove the remote device object at the given path.""" + try: + self._adapter.RemoveDevice(device) + except dbus.exceptions.DBusException as msg: + logging.error(msg) + raise SystemExit(1) + logger.info('Device properly removed') + + +class BtGATTRemoteService: + """Bluetooth LE GATT Remote Service class.""" + def __init__(self, SVC_UUID, adapter, device, max_notif): + self.SVC_UUID = SVC_UUID + self._adapter = adapter + self.device = device + self._max_notif = max_notif + self._notifications = 0 + self._bus = dbus.SystemBus() + self._manager = dbus.Interface( + self._bus.get_object("org.bluez", "/"), OM_INTERFACE) + self._main_loop = GObject.MainLoop() + self._service = self._find_service() + self._path = self._service.object_path + + def _get_managed_objects(self): + return self._manager.GetManagedObjects() + + def _find_service(self): + for path, ifaces in self._get_managed_objects().items(): + if GATT_SERVICE_INTERFACE not in ifaces.keys(): + continue + service = self._bus.get_object('org.bluez', path) + props = dbus.Interface(service, PROP_INTERFACE) + if props.Get(GATT_SERVICE_INTERFACE, "UUID") == self.SVC_UUID: + logger.info('Service found: {}'.format(path)) + return service + self._adapter.remove_device(self._device) + raise SystemExit("Bluetooth Service not found!") + + def find_chrc(self, MSRMT_UUID): + for path, ifaces in self._get_managed_objects().items(): + if GATT_CHRC_INTERFACE not in ifaces.keys(): + continue + chrc = self._bus.get_object('org.bluez', path) + props = dbus.Interface(chrc, PROP_INTERFACE) + if props.Get(GATT_CHRC_INTERFACE, "UUID") == MSRMT_UUID: + logger.info('Characteristic found: {}'.format(path)) + return chrc + self._adapter.remove_device(self._device) + raise SystemExit("Bluetooth Characteristic not found!") + + def _generic_error_cb(self, error): + self._adapter.remove_device(self._device) + self._main_loop.quit() + raise SystemExit('D-Bus call failed: ' + str(error)) + + def _start_notify_cb(self): + logger.info('Notifications enabled') + + def _notify_timeout(self): + self._adapter.remove_device(self._device) + self._main_loop.quit() + raise SystemExit('Notification test failed') + + def _changed_cb(self, iface, changed_props, invalidated_props): + if iface != GATT_CHRC_INTERFACE: + return + if not len(changed_props): + return + value = changed_props.get('Value', None) + if not value: + return + logger.debug('New Notification') + self._notifications += 1 + if self._notifications >= self._max_notif: + logger.info('Notification test succeeded') + self._main_loop.quit() + + def check_notification(self, chrc, timeout=20): + # Listen to PropertiesChanged signals from the BLE Measurement + # Characteristic. + prop_iface = dbus.Interface(chrc, PROP_INTERFACE) + prop_iface.connect_to_signal("PropertiesChanged", self._changed_cb) + + # Subscribe to BLE Measurement notifications. + chrc.StartNotify(reply_handler=self._start_notify_cb, + error_handler=self._generic_error_cb, + dbus_interface=GATT_CHRC_INTERFACE) + GObject.timeout_add_seconds(timeout, self._notify_timeout) + self._main_loop.run() + + +def main(): + logger.setLevel(logging.DEBUG) + parser = argparse.ArgumentParser() + parser.add_argument( + "id", + help='Address, udev path or name (hciX) of the BT adapter') + parser.add_argument( + "ADV_SVC_UUID", help='Beacon Gatt configuration service UUID') + parser.add_argument( + "SVC_UUID", help='Beacon Gatt notification service UUID') + parser.add_argument("MSRMT_UUID", help='Beacon Gatt measurement UUID') + parser.add_argument( + "--max-notif", "-m", type=int, default=5, + help="Maximum notification threshold") + args = parser.parse_args() + adapter = BtAdapter(args.id) + adapter.ensure_powered() + adapter.scan() + device = adapter.find_device_with_service(args.ADV_SVC_UUID) + try: + device.Connect() + except dbus.exceptions.DBusException as msg: + logging.error(msg) + adapter.remove_device(device) + raise SystemExit(1) + logger.info('Device connected, waiting 10s for services to be available') + time.sleep(10) # Let all the services to broadcast their UUIDs + service = BtGATTRemoteService( + args.SVC_UUID, adapter, device, args.max_notif) + chrc = service.find_chrc(args.MSRMT_UUID) + service.check_notification(chrc) + try: + device.Disconnect() + except dbus.exceptions.DBusException as msg: + logging.error(msg) + adapter.remove_device(device) + raise SystemExit(1) + logger.info('Device properly disconnected') + adapter.remove_device(device) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/gpu_test b/bin/gpu_test index bd22acc..49ea31a 100755 --- a/bin/gpu_test +++ b/bin/gpu_test @@ -23,11 +23,13 @@ lockups. Inspired by the workload directory of the xdiagnose package. """ +import gi import os import re import subprocess import sys import time +gi.require_version('Gio', '2.0') from gi.repository import Gio from math import cos, sin from threading import Thread diff --git a/bin/graphics_driver b/bin/graphics_driver index dfb5522..6d28f5a 100755 --- a/bin/graphics_driver +++ b/bin/graphics_driver @@ -361,7 +361,10 @@ def hybrid_graphics_check(xlog): def main(): - xlog = XorgLog("/var/log/Xorg.0.log") + if os.path.isfile("/var/log/Xorg.0.log"): + xlog = XorgLog("/var/log/Xorg.0.log") + else: + xlog = XorgLog(os.path.expanduser("~/.local/share/xorg/Xorg.0.log")) results = [] results.append(get_driver_info(xlog)) diff --git a/bin/graphics_env b/bin/graphics_env index 9511560..89c761d 100755 --- a/bin/graphics_env +++ b/bin/graphics_env @@ -19,7 +19,7 @@ if [[ $DRIVER == "amdgpu" || $DRIVER == "radeon" ]]; 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 ! grep -q DRI3 /var/log/Xorg.0.log; then + 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} diff --git a/bin/gst_pipeline_test b/bin/gst_pipeline_test index cde7fbb..4d9f3c4 100755 --- a/bin/gst_pipeline_test +++ b/bin/gst_pipeline_test @@ -1,11 +1,14 @@ #!/usr/bin/env python3 from argparse import ArgumentParser +import gi import logging import re import os import sys import time +gi.require_version('Gst', '1.0') +gi.require_version('GLib', '2.0') from gi.repository import Gst from gi.repository import GLib from subprocess import check_output diff --git a/bin/ipmi_test b/bin/ipmi_test index 8be8b96..71e7f36 100755 --- a/bin/ipmi_test +++ b/bin/ipmi_test @@ -28,13 +28,15 @@ done echo echo "Checking for chassis status" ipmitool chassis status && echo "Successfully got chassis status" && chassis=0 || chassis=1 -echo "Checking to see if we can get sensor data" -ipmitool sdr list full && echo "Successfully got sensor data" && sensor=0 || sensor=1 +echo "Checking to see if we can get power status" +ipmitool power status && echo "Successfully got power status" && power=0 || power=1 +echo "Checking to see if we can get user data" +ipmitool user list && echo "Successfully got user data" && user=0 || user=1 echo "Checking to see if we can get info on the BMC" ipmitool bmc info && echo "Successfully got BMC information" && bmc=0 || bmc=1 # if everything passes, exit 0 -[ $chassis -eq 0 ] && [ $sensor -eq 0 ] && [ $bmc -eq 0 ] && exit 0 || echo "FAILURE: chassis: $chassis sensor: $sensor bmc: $bmc" +[ $chassis -eq 0 ] && [ $power -eq 0 ] && [ $user -eq 0 ] && [ $bmc -eq 0 ] && exit 0 || echo "FAILURE: chassis: $chassis power: $power user: $user bmc: $bmc" # otherwise exit 1 exit 1 diff --git a/bin/key_test b/bin/key_test index 3cf7ae0..2bd95f9 100755 --- a/bin/key_test +++ b/bin/key_test @@ -18,6 +18,7 @@ # along with Checkbox. If not, see <http://www.gnu.org/licenses/>. +import gi import os import sys @@ -27,6 +28,7 @@ import struct import termios from gettext import gettext as _ + from gi.repository import GObject from optparse import OptionParser @@ -221,6 +223,8 @@ class GtkReporter(Reporter): def __init__(self, *args, **kwargs): super(GtkReporter, self).__init__(*args, **kwargs) + gi.require_version('Gdk', '3.0') + gi.require_version("Gtk", "3.0") from gi.repository import Gdk, Gtk # Initialize GTK constants diff --git a/bin/keyboard_test b/bin/keyboard_test index 309b845..1615106 100755 --- a/bin/keyboard_test +++ b/bin/keyboard_test @@ -33,8 +33,10 @@ def cli_prompt(): def gtk_prompt(): + import gi + gi.require_version('Gdk', '3.0') + gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gdk - # create a new window window = Gtk.Window() window.set_type_hint(Gdk.WindowType.TOPLEVEL) diff --git a/bin/lock_screen_watcher b/bin/lock_screen_watcher index 4e9847e..1c30270 100755 --- a/bin/lock_screen_watcher +++ b/bin/lock_screen_watcher @@ -17,7 +17,9 @@ # along with Checkbox. If not, see <http://www.gnu.org/licenses/>. import dbus +import gi from dbus.mainloop.glib import DBusGMainLoop +gi.require_version('GLib', '2.0') from gi.repository import GObject from gi.repository import GLib diff --git a/bin/manage_compiz_plugin b/bin/manage_compiz_plugin index a1236ea..9650a9d 100755 --- a/bin/manage_compiz_plugin +++ b/bin/manage_compiz_plugin @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # This file is part of Checkbox. # -# Copyright 2014-2015 Canonical Ltd. +# Copyright 2014-2018 Canonical Ltd. # Written by: # Daniel Manrique <roadmr@ubuntu.com> # Sylvain Pineau <sylvain.pineau@canonical.com> @@ -33,14 +33,13 @@ import sys import subprocess import time -PATH="org.compiz.core:/org/compiz/profiles/unity/plugins/core/" -KEY="active-plugins" +KEY="/org/compiz/profiles/unity/plugins/core/active-plugins" gettext.textdomain("com.canonical.certification.checkbox") gettext.bindtextdomain("com.canonical.certification.checkbox", os.getenv("CHECKBOX_PROVIDER_LOCALE_DIR", None)) -plugins = eval(subprocess.check_output(["gsettings", "get", PATH, KEY])) +plugins = eval(subprocess.check_output(["dconf", "read", KEY])) parser = argparse.ArgumentParser(description=_("enable/disable compiz plugins"), epilog=_("Available plugins: {}").format(plugins)) @@ -58,6 +57,6 @@ else: if args.plugin not in plugins: raise SystemExit(_("Plugin {} doesn't exist").format(args.plugin)) plugins.remove(args.plugin) -subprocess.call(["gsettings", "set", PATH, KEY, str(plugins)]) +subprocess.call(["dconf", "write", KEY, str(plugins)]) time.sleep(3) diff --git a/bin/net_if_watcher.py b/bin/net_if_watcher.py new file mode 100755 index 0000000..0ba61df --- /dev/null +++ b/bin/net_if_watcher.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# Copyright 2018 Canonical Ltd. +# All rights reserved. +# +# Written by: +# Maciej Kisielewski <maciej.kisielewski@canonical.com> +""" +Detect insertion of a new network interface. +""" + +from pathlib import Path +import time + + +def get_ifaces(): + return set([i.name for i in Path("/sys/class/net").iterdir()]) + + +def main(): + print("INSERT NOW") + starting_ifaces = get_ifaces() + attempts = 20 + while attempts > 0: + now_ifaces = get_ifaces() + # check if something disappeared + if not starting_ifaces == now_ifaces & starting_ifaces: + raise SystemExit("Interface(s) disappeared: {}".format( + ", ".join(list(starting_ifaces - now_ifaces)))) + new_ifaces = now_ifaces - starting_ifaces + if new_ifaces: + print() + print("New interface(s) detected: {}".format( + ", ".join(list(new_ifaces)))) + return + time.sleep(1) + print('.', end='', flush=True) + attempts -= 1 + print() + raise SystemExit("Failed to detect new network interface") + + +if __name__ == '__main__': + main() diff --git a/bin/network_restart b/bin/network_restart index 31f1da7..d01e983 100755 --- a/bin/network_restart +++ b/bin/network_restart @@ -14,6 +14,9 @@ from subprocess import check_output, check_call, CalledProcessError, STDOUT from argparse import ArgumentParser try: + import gi + gi.require_version('GLib', '2.0') + gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GObject, GLib GLib.threads_init() GObject.threads_init() diff --git a/bin/pm_test b/bin/pm_test index d1bd3b1..70cd121 100755 --- a/bin/pm_test +++ b/bin/pm_test @@ -1,4 +1,10 @@ #!/usr/bin/env python3 +""" +If you're debugging this program, set PM_TEST_DRY_RUN in your environment. +It will make the script not run actual S3, S4, reboot and poweroff commands. +""" +import gi +import json import logging import logging.handlers import os @@ -13,7 +19,7 @@ from calendar import timegm from configparser import ConfigParser from datetime import datetime, timedelta from time import localtime, time - +gi.require_version("Gtk", "3.0") from gi.repository import GObject, Gtk @@ -41,8 +47,13 @@ def main(): logging.debug('Arguments: {0!r}'.format(args)) logging.debug('Extra Arguments: {0!r}'.format(extra_args)) + dry_run = os.environ.get('PM_TEST_DRY_RUN', False) + if dry_run: + logging.info("Running in dry-run mode") + try: - operation = PowerManagementOperation(args, extra_args, user=username) + operation = PowerManagementOperation( + args, extra_args, user=username, dry_run=dry_run) operation.setup() operation.run() except (TestCancelled, TestFailed) as exception: @@ -64,10 +75,11 @@ def main(): class PowerManagementOperation(): SLEEP_TIME = 5 - def __init__(self, args, extra_args, user=None): + def __init__(self, args, extra_args, user=None, dry_run=False): self.args = args self.extra_args = extra_args self.user = user + self.dry_run = dry_run def setup(self): """ @@ -128,11 +140,17 @@ class PowerManagementOperation(): logging.info('Executing new {0!r} operation...' .format(self.args.pm_operation)) logging.debug('Executing: {0!r}...'.format(command_str)) - # The PM operation is performed asynchronously so let's just wait - # indefinitely until it happens and someone comes along to kill us. - # This addresses LP: #1413134 - subprocess.check_call(command_str, shell=True) - signal.pause() + if self.dry_run: + print("\n\nRUNNING IN DRY-RUN MODE") + print("Normally the program would run: {}".format(command_str)) + print("Waiting for Enter instead") + input() + else: + # The PM operation is performed asynchronously so let's just wait + # indefinitely until it happens and someone comes along to kill us. + # This addresses LP: #1413134 + subprocess.check_call(command_str, shell=True) + signal.pause() def run_suspend_cycles(self, cycles_count, fwts): """Run suspend and resume cycles.""" @@ -155,15 +173,21 @@ class PowerManagementOperation(): command_str = command_tpl.format(script_path, cycles_count) logging.info('Running suspend/resume cycles') logging.debug('Executing: {0!r}...'.format(command_str)) - try: - # We call sleep_test or fwts_test script and log its output as it - # contains average times we need to compute global average times - # later. - logging.info(subprocess.check_output( - command_str, universal_newlines=True, shell=True)) - except subprocess.CalledProcessError as e: - logging.error('Error while running {0}:'.format(e.cmd)) - logging.error(e.output) + if self.dry_run: + print("\n\nRUNNING IN DRY-RUN MODE") + print("Normally the program would run: {}".format(command_str)) + print("Waiting for Enter instead") + input() + else: + try: + # We call sleep_test or fwts_test script and log its output as + # it contains average times we need to compute global average + # times later. + logging.info(subprocess.check_output( + command_str, universal_newlines=True, shell=True)) + except subprocess.CalledProcessError as e: + logging.error('Error while running {0}:'.format(e.cmd)) + logging.error(e.output) def summary(self): """ @@ -199,6 +223,29 @@ class PowerManagementOperation(): message = ('{0} test complete' .format(self.args.pm_operation.capitalize())) + total_suspends_expected = ( + self.args.suspends_before_reboot * self.args.total) + problems = '' + fwts_log_path = os.path.join(self.args.log_dir, 'fwts.log') + try: + with open(fwts_log_path, 'rt') as f: + magic_line = 'Completed S3 cycle(s) \n' + count = f.readlines().count(magic_line) + if count != total_suspends_expected: + problems = ( + "Found {} occurrences of '{}'. Expected {}".format( + count, magic_line.strip(), + total_suspends_expected)) + except FileNotFoundError: + problems = "Error opening {}".format(fwts_log_path) + if problems: + result = { + 'outcome': 'fail' if problems else 'pass', + 'comment': problems, + } + with open(os.path.join(self.args.log_dir, '__result'), 'wt') as f: + json.dump(result, f) + if self.args.silent: logging.info(message) else: @@ -206,6 +253,8 @@ class PowerManagementOperation(): MessageDialog(title, message).run() if self.args.checkbox_respawn_cmd: subprocess.run( + r'unset LD_LIBRARY_PATH;' + r'unset PYTHONPATH; unset PYTHONHOME;' r'DISPLAY=:0 x-terminal-emulator -e "sudo -u ' r'{} bash -c \"source {}; exec bash\""'.format( self.user, self.args.checkbox_respawn_cmd), shell=True) @@ -572,11 +621,11 @@ autologin-user-timeout=0 shutil.copystat(self.config_filename, backup_filename) break - with open(self.config_filename, 'w') as f: - if self.config_filename == '/etc/lightdm/lightdm.conf': - f.write(self.template.format(username=self.user)) - elif self.config_filename == '/etc/gdm3/custom.conf': - self.parser.write(f) + with open(self.config_filename, 'w') as f: + if self.config_filename == '/etc/lightdm/lightdm.conf': + f.write(self.template.format(username=self.user)) + elif self.config_filename == '/etc/gdm3/custom.conf': + self.parser.write(f) def disable(self): """ diff --git a/bin/removable_storage_test b/bin/removable_storage_test index 42e197a..6de3f97 100755 --- a/bin/removable_storage_test +++ b/bin/removable_storage_test @@ -841,20 +841,10 @@ def main(): # This will raise KeyError for no # associated disk device was found. xhci_disks = test.get_disks_xhci() - # pep8 style suggest to limit the try clause - # to the absolute minimum amount of code necessary - try: - disk_xhci_flag = xhci_disks[disk] - except KeyError: - print("\t\tDisk does not use xhci_hci.") - return 1 - else: - if('xhci' == disk_xhci_flag): - print("\t\tDriver Detected: xhci_hcd") - else: - print("\t\tDisk does not use xhci_hci.") - logging.debug("disk_xhci_flag is not xhci") - return 1 + if test.get_disks_xhci().get(disk, '') != 'xhci': + raise SystemExit( + "\t\tDisk does not use xhci_hcd.") + print("\t\tDriver Detected: xhci_hcd") else: # Give it a hint for the detection failure. # LP: #1362902 diff --git a/bin/resolution_test b/bin/resolution_test index a50868b..55a7828 100755 --- a/bin/resolution_test +++ b/bin/resolution_test @@ -1,9 +1,10 @@ #!/usr/bin/env python3 +import gi import sys from argparse import ArgumentParser - +gi.require_version('Gdk', '3.0') from gi.repository import Gdk diff --git a/bin/socketcan_test.py b/bin/socketcan_test.py new file mode 100755 index 0000000..232fc2c --- /dev/null +++ b/bin/socketcan_test.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +# This file is part of Checkbox. +# +# Copyright 2018 Canonical Ltd. +# Written by: +# Jonathan Cave <jonathan.cave@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 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. + +import argparse +import os +import socket +import struct +import sys +import textwrap +import threading +import time + + +class CANSocket(): + + # struct module format strings for CAN packets + # Normal format: + # < little-endian + # I unsigned int (4) : CAN-ID + EFF/RTR/ERR Flags + # B unsigned char (1) : Data length + # 3x padding (3 * 1) : - + # 8s char array (8 * 1) : Data + FORMAT = "<IB3x8s" + # Flexible Data (FD) rate format: + # < little-endian + # I unsigned int (4) : CAN-ID + EFF/RTR/ERR Flags + # B unsigned char (1) : Data length + # B unsigned char (1) : FD Flags + # 2x padding (2 * 1) : - + # 64s char array (64 * 1) : Data + FD_FORMAT = "<IBB2x64s" + + CAN_MTU = struct.Struct(FORMAT).size + CANFD_MTU = struct.Struct(FD_FORMAT).size + + # Socket options from <linux/can/raw.h> + CAN_RAW_FILTER = 1 # set 0 .. n can_filter(s) + CAN_RAW_ERR_FILTER = 2 # set filter for error frames + CAN_RAW_LOOPBACK = 3 # local loopback (default:on) + CAN_RAW_RECV_OWN_MSGS = 4 # receive my own msgs (default:off) + CAN_RAW_FD_FRAMES = 5 # allow CAN FD frames (default:off) + CAN_RAW_JOIN_FILTERS = 6 # all filters must match to trigger + + def __init__(self, interface=None, fdmode=False, loopback=True): + self.sock = socket.socket(socket.PF_CAN, # protocol family + socket.SOCK_RAW, + socket.CAN_RAW) + self._fdmode = fdmode + self._loopback = loopback + if interface is not None: + self._bind(interface) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + self.sock.close() + + def _bind(self, interface): + self.sock.bind((interface,)) + if self._fdmode: # default is off + self.sock.setsockopt(socket.SOL_CAN_RAW, self.CAN_RAW_FD_FRAMES, 1) + if not self._loopback: # default is on + self.sock.setsockopt(socket.SOL_CAN_RAW, self.CAN_RAW_LOOPBACK, 0) + + def send(self, can_id, data, id_flags=0, fd_flags=0): + can_id = can_id | id_flags + if self._fdmode: + can_pkt = struct.pack(self.FD_FORMAT, can_id, len(data), fd_flags, + data) + else: + can_pkt = struct.pack(self.FORMAT, can_id, len(data), data) + self.sock.send(can_pkt) + + def recv(self): + if self._fdmode: + can_pkt = self.sock.recv(self.CANFD_MTU) + can_id, length, fd_flags, data = struct.unpack(self.FD_FORMAT, + can_pkt) + else: + can_pkt = self.sock.recv(self.CAN_MTU) + can_id, length, data = struct.unpack(self.FORMAT, can_pkt) + can_id &= socket.CAN_EFF_MASK + return (can_id, data[:length]) + + +def echo_test(args): + # ID conversion and size check + print('Using source ID: {}'.format(args.can_id)) + can_id_i = int(args.can_id, 16) + if can_id_i > 2047 and not args.effid: + raise SystemExit('ERROR: CAN ID to high for SFF') + id_flags = 0 + if args.effid: + print('Setting EFF CAN ID flag') + id_flags = socket.CAN_EFF_FLAG + + # Whether to enable local loopback, required for local only test + # but only want to parse packets from other end if remote + loopback = not args.remote + + # Default data size is 8 bytes but if testing FD Mode use 64 + data_size = 8 + if args.fdmode: + data_size = 64 + data_b = os.urandom(data_size) + print('Sending data: {}'.format(data_b.hex())) + + recv_id_i = None + recv_data_b = None + + def receive(): + nonlocal recv_id_i + nonlocal recv_data_b + print('Opening read socket on {}'.format(args.interface)) + with CANSocket(args.interface, fdmode=args.fdmode, + loopback=loopback) as recv_s: + recv_id_i, recv_data_b = recv_s.recv() + + # Create a receive thread + recv_t = threading.Thread(target=receive, daemon=True) + recv_t.start() + time.sleep(1) + + print('Opening send socket on {}'.format(args.interface)) + # Open socket, will raise OSError on failure + with CANSocket(args.interface, fdmode=args.fdmode, + loopback=loopback) as send_s: + print('Sending data...', flush=True) + try: + send_s.send(can_id_i, data_b, id_flags=id_flags) + except OSError as e: + print(e, file=sys.stderr) + if e.errno == 90: + raise SystemExit('ERROR: interface does not support FD Mode') + else: + raise SystemExit('ERROR: OSError on attempt to send') + + recv_t.join(10) + if recv_t.is_alive(): + raise SystemExit('ERROR: Timeout waiting to receive data') + + print('Received packet') + print(' ID : {:x}'.format(recv_id_i)) + print(' Data: {}'.format(recv_data_b.hex())) + if recv_id_i != can_id_i or recv_data_b != data_b: + raise SystemExit('ERROR: ID/Data received does not match sent') + + print('\nPASSED') + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description='SocketCAN Tests', + epilog=textwrap.dedent(''' + Examples: + socketcan_test.py can0 123 + socketcan_test.py can0 212 --remote + socketcan_test.py can0 FA123 --effid + socketcan_test.py can0 E407DB --effid --fdmode''').lstrip()) + parser.add_argument('interface', type=str, help='Interface name e.g. can0') + parser.add_argument('can_id', type=str, help=textwrap.dedent(''' + CAN ID of source in Hex, max of 11 bits using Standard Frame + Format (SFF). Specifying use of Extended Frame Format (EFF) + allows the use of up to 29 bit IDs.''').lstrip()) + parser.add_argument('--remote', action='store_true', + help='Expect a remote device to echo the test packet') + parser.add_argument('--effid', action='store_true', + help='Use EFF ID (CAN 2.0 B)') + parser.add_argument('--fdmode', action='store_true', + help='Attempt to send 64 bytes of data i.e. FD mode') + parser.set_defaults(func=echo_test) + + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/bin/touchpad_test b/bin/touchpad_test index 7152745..bc6a474 100755 --- a/bin/touchpad_test +++ b/bin/touchpad_test @@ -1,9 +1,13 @@ #!/usr/bin/env python3 +import gi import sys import gettext from gettext import gettext as _ +gi.require_version('Gdk', '3.0') +gi.require_version('Gio', '2.0') +gi.require_version("Gtk", "3.0") from gi.repository import Gio, Gtk, Gdk from optparse import OptionParser diff --git a/bin/xrandr_cycle b/bin/xrandr_cycle index b9f615b..34f953c 100755 --- a/bin/xrandr_cycle +++ b/bin/xrandr_cycle @@ -87,6 +87,13 @@ for adapter, mode in modes: highest_modes = [] for adapter, params in top_res_per_aspect.items(): for aspect, width in params.items(): + # xrandr can list modes that are unsupported, unity-control-center + # defines minimum width and height, below which the resolution + # is not listed as a choice in display settings panel in UCC + # see should_show_resolution function in cc-display-panel.c + # from lp:unity-control-center + if width < 675 or width / aspect < 530: + continue mode = '{}x{}'.format(width, width/aspect) highest_modes.append((adapter, mode)) diff --git a/debian/.git-dpm b/debian/.git-dpm index 24742a6..9b320b0 100644 --- a/debian/.git-dpm +++ b/debian/.git-dpm @@ -1,8 +1,8 @@ # see git-dpm(1) from git-dpm package -22b317e6019e8aa8f46783bb17d59b220a764fbd -22b317e6019e8aa8f46783bb17d59b220a764fbd -22b317e6019e8aa8f46783bb17d59b220a764fbd -22b317e6019e8aa8f46783bb17d59b220a764fbd -plainbox-provider-checkbox_0.45.0.orig.tar.gz -f48a6319d5cdc242ef34c6fce7dbce58c88d90a2 -1739413 +6ca200fedf88bf8fba57b3f2eda59799d6f574cd +6ca200fedf88bf8fba57b3f2eda59799d6f574cd +6ca200fedf88bf8fba57b3f2eda59799d6f574cd +6ca200fedf88bf8fba57b3f2eda59799d6f574cd +plainbox-provider-checkbox_0.46.0~rc1.orig.tar.gz +2385f8bb672cc0c6552661f065f9d55b817f3bfb +1745746 @@ -5,7 +5,7 @@ from plainbox.provider_manager import N_ setup( name='plainbox-provider-checkbox', namespace='com.canonical.certification', - version="0.45.0", + version="0.46.0rc1", description=N_("Checkbox provider"), gettext_domain='plainbox-provider-checkbox', strict=False, deprecated=False, diff --git a/units/audio/jobs.pxu b/units/audio/jobs.pxu index fa75447..120a9bd 100644 --- a/units/audio/jobs.pxu +++ b/units/audio/jobs.pxu @@ -49,6 +49,7 @@ requires: 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 @@ -77,6 +78,7 @@ requires: 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 @@ -106,6 +108,7 @@ requires: 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 @@ -135,6 +138,7 @@ requires: 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 @@ -165,6 +169,7 @@ requires: 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 diff --git a/units/bluetooth/jobs.pxu b/units/bluetooth/jobs.pxu index 0a246df..1f85fa6 100644 --- a/units/bluetooth/jobs.pxu +++ b/units/bluetooth/jobs.pxu @@ -132,7 +132,7 @@ id: bluetooth/file_transfer_stress estimated_duration: 300.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] @@ -194,3 +194,23 @@ _steps: 3. After it's paired and connected, enter some text with your keyboard and close the small input test tool. _verification: Did the Bluetooth Smart keyboard work as expected? + +unit: template +template-resource: device +template-filter: device.category == 'BLUETOOTH' +template-engine: jinja2 +template-unit: job +id: bluetooth4/beacon_notification_{{ path }} +_summary: Test system can get beacon notifications on the {{ path.split('/')[-1] }} adapter +environ: ADV_SVC_UUID SVC_UUID MSRMT_UUID +command: + gatt-notify-test.py {{ path.split('/')[-1] }} $ADV_SVC_UUID $SVC_UUID $MSRMT_UUID +plugin: shell +user: root +category_id: com.canonical.plainbox::bluetooth +estimated_duration: 30 +requires: + package.name == 'bluez' or snap.name == 'bluez' + {%- if __on_ubuntucore__ %} + connections.slot == 'bluez:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:bluez' + {% endif -%} diff --git a/units/bluetooth/test-plan.pxu b/units/bluetooth/test-plan.pxu index ae2c305..498a4ce 100644 --- a/units/bluetooth/test-plan.pxu +++ b/units/bluetooth/test-plan.pxu @@ -14,10 +14,10 @@ _name: Bluetooth tests (Manual) _description: Bluetooth tests (Manual) include: - bluetooth/audio-a2dp certification-status=blocker + bluetooth/audio-a2dp certification-status=non-blocker bluetooth/HID - bluetooth4/HOGP-mouse certification-status=non-blocker - bluetooth4/HOGP-keyboard certification-status=non-blocker + bluetooth4/HOGP-mouse certification-status=blocker + bluetooth4/HOGP-keyboard certification-status=blocker id: bluetooth-cert-automated @@ -32,5 +32,5 @@ unit: test plan _name: Bluetooth tests (certification blockers only) _description: Bluetooth tests (certification blockers only) include: - bluetooth/detect-output certification-status=blocker - bluetooth/audio-a2dp certification-status=blocker + bluetooth4/HOGP-mouse certification-status=blocker + bluetooth4/HOGP-keyboard certification-status=blocker diff --git a/units/camera/jobs.pxu b/units/camera/jobs.pxu index 4db5222..4219462 100644 --- a/units/camera/jobs.pxu +++ b/units/camera/jobs.pxu @@ -4,7 +4,8 @@ id: camera/detect estimated_duration: 1.2 requires: device.category == 'CAPTURE' -command: if [ "`lsb_release -c | awk {'print $2'}`" == "precise" ]; then camera_test_legacy detect; else camera_test detect; fi +command: + camera_test detect _description: This Automated test attempts to detect a camera. plugin: user-interact-verify @@ -14,7 +15,8 @@ estimated_duration: 120.0 depends: camera/detect requires: device.category == 'CAPTURE' -command: if [ "`lsb_release -c | awk {'print $2'}`" == "precise" ]; then camera_test_legacy display; else camera_test display; fi +command: + camera_test display _description: PURPOSE: This test will check that the built-in camera works @@ -24,16 +26,23 @@ _description: Did you see the video capture? plugin: user-interact-verify +template-engine: jinja2 category_id: com.canonical.plainbox::camera id: camera/still estimated_duration: 120.0 depends: camera/detect requires: - package.name == 'gir1.2-gst-plugins-base-0.10' or package.name == 'gir1.2-gst-plugins-base-1.0' + {%- if __on_ubuntucore__ %} + executable.name == 'fswebcam' + device.category == 'CAPTURE' + {%- else %} + package.name == 'gir1.2-gst-plugins-base-1.0' package.name == 'eog' - package.name == 'fswebcam' or package.name == 'gir1.2-gst-plugins-base-0.10' or package.name == 'gir1.2-gst-plugins-base-1.0' + package.name == 'fswebcam' or package.name == 'gir1.2-gst-plugins-base-1.0' device.category == 'CAPTURE' -command: if [ "`lsb_release -c | awk {'print $2'}`" == "precise" ]; then camera_test_legacy still; else camera_test still; fi + {% endif -%} +command: + camera_test still _description: PURPOSE: This test will check that the built-in camera works @@ -43,14 +52,21 @@ _description: Did you see the image? plugin: shell +template-engine: jinja2 category_id: com.canonical.plainbox::camera id: camera/multiple-resolution-images estimated_duration: 1.2 depends: camera/detect requires: - package.name == 'fswebcam' or package.name == 'gir1.2-gst-plugins-base-0.10' or package.name == 'gir1.2-gst-plugins-base-1.0' + {%- if __on_ubuntucore__ %} + executable.name == 'fswebcam' + device.category == 'CAPTURE' + {%- else %} + package.name == 'fswebcam' or package.name == 'gir1.2-gst-plugins-base-1.0' device.category == 'CAPTURE' -command: if [ "`lsb_release -c | awk {'print $2'}`" == "precise" ]; then camera_test_legacy resolutions; else camera_test resolutions; fi + {% endif -%} +command: + camera_test resolutions _description: Takes multiple pictures based on the resolutions supported by the camera and validates their size and that they are of a valid format. diff --git a/units/cpu/jobs.pxu b/units/cpu/jobs.pxu index db0dd8f..a64757e 100644 --- a/units/cpu/jobs.pxu +++ b/units/cpu/jobs.pxu @@ -77,7 +77,7 @@ _summary: Test offlining of each CPU core _description: Attempts to offline each core in a multicore system. -requires: cpuinfo.platform not in ("aarch64", "armhf") +requires: cpuinfo.platform not in ("aarch64", "armv7l") plugin: shell category_id: com.canonical.plainbox::cpu @@ -100,18 +100,37 @@ command: nice -n -20 frequency_governors_test --debug _description: This test checks that CPU frequency governors are obeyed when set. +unit: template +template-resource: cpuinfo +template-filter: cpuinfo.platform == 'armv7l' +template-unit: job plugin: shell category_id: com.canonical.plainbox::cpu -id: cpu/arm_vfp_support +id: cpu/arm_vfp_support_{platform} estimated_duration: 1.0 -requires: - cpuinfo.platform in ("armv7l", "aarch64") user: root -command: grep VFP /var/log/syslog +command: + echo "{other}" | grep "vfp\|vfpv3\|vfpv4\|vfpd32" +_summary: + Validate that the Vector Floating Point Unit is running on {platform} device +_description: + Validate that the Vector Floating Point Unit is running on {platform} device. + +unit: template +template-resource: cpuinfo +template-filter: cpuinfo.platform == 'aarch64' +template-unit: job +plugin: shell +category_id: com.canonical.plainbox::cpu +id: cpu/arm_vfp_support_{platform} +estimated_duration: 1.0 +user: root +command: + echo "{other}" | grep "fp" _summary: - Validate that the Vector Floating Point Unit is running on ARM device + Validate that the Floating Point Unit is running on {platform} device _description: - Validate that the Vector Floating Point Unit is running on ARM device. + Validate that the Floating Point Unit is running on {platform} device. plugin:shell id: cpu/cstates diff --git a/units/disk/encryption.pxu b/units/disk/encryption.pxu new file mode 100644 index 0000000..b7d41c8 --- /dev/null +++ b/units/disk/encryption.pxu @@ -0,0 +1,21 @@ + +id: disk/encryption/detect +category_id: com.canonical.plainbox::disk +plugin: shell +template-engine: jinja2 +user: root +requires: + executable.name == 'lsblk' + executable.name == 'dmsetup' + executable.name == 'cryptsetup' +_summary: Test that Full Disk Encryption is in use +_description: + Examine the system to detect if one of the standard full disk encryption + implementations is in use +command: + {%- if __on_ubuntucore__ %} + fde_tests.py + {%- else %} + fde_tests.py desktop + {% endif -%} +estimated_duration: 2.0 \ No newline at end of file diff --git a/units/disk/jobs.pxu b/units/disk/jobs.pxu index 072ac52..3de5532 100644 --- a/units/disk/jobs.pxu +++ b/units/disk/jobs.pxu @@ -21,7 +21,7 @@ flags: deprecated user: root estimated_duration: 10.0 command: disk_stats_test {name} -_summary: Disk statistics for {product} +_summary: Disk statistics for {product_slug} _description: This test checks disk stats, generates some activity and rechecks stats to verify they've changed. It also verifies that disks appear in the various @@ -29,7 +29,7 @@ _description: . This test will inspect the following disk: . - product name: {product} + product name: {product_slug} sysfs path: {path} device node path: /dev/{name} @@ -44,9 +44,9 @@ requires: package.name == 'smartmontools' or executable.name == 'smartctl' block_device.smart == 'True' and block_device.name == '{name}' _summary: - Test SMART capabilities for {product} + Test SMART capabilities for {product_slug} _description: - This tests the SMART capabilities for {product} (Note that this test may not work against hardware RAID) + This tests the SMART capabilities for {product_slug} (Note that this test may not work against hardware RAID) user: root command: disk_smart -b /dev/{name} -s 130 -t 530 @@ -58,9 +58,10 @@ category_id: com.canonical.plainbox::disk id: disk/read_performance_{name} estimated_duration: 65.0 requires: -_summary: Disk performance test for {product} +_summary: Disk performance test for {product_slug} _description: Verify that disk storage performs at or above baseline performance user: root +environ: DISK_READ_PERF command: disk_read_performance_test {name} unit: template @@ -72,7 +73,7 @@ id: disk/storage_device_{name} estimated_duration: 375.0 user: root requires: -_summary: Disk I/O stress test for {product} +_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} @@ -87,8 +88,8 @@ user: root requires: package.name == 'stress-ng' or executable.name == 'stress-ng' package.name == 'uuid-runtime' or executable.name == 'uuidgen' -_summary: Disk stress_ng test for {product} -_description: Disk stress_ng test for {product} +_summary: Disk stress_ng test for {product_slug} +_description: Disk stress_ng test for {product_slug} command: disk_stress_ng {name} --base-time 240 --really-run unit: template @@ -102,8 +103,8 @@ user: root requires: block_device.state != 'removable' and block_device.name == '{name}' package.name == 'bc' or executable.name == 'bc' -_summary: Check of CPU load imposed by {product} -_description: Check to ensure CPU load of {product} is not too high +_summary: Check of CPU load imposed by {product_slug} +_description: Check to ensure CPU load of {product_slug} is not too high command: disk_cpu_load {name} plugin: shell @@ -124,7 +125,7 @@ id: disk/hdd-parking estimated_duration: 60.0 requires: device.category == 'DISK' - package.name == 'hdapsd' + executable.name == 'hdapsd' depends: input/accelerometer user: root command: hdd_parking diff --git a/units/disk/test-plan.pxu b/units/disk/test-plan.pxu index 08974cb..b080966 100644 --- a/units/disk/test-plan.pxu +++ b/units/disk/test-plan.pxu @@ -27,3 +27,24 @@ include: disk/detect certification-status=blocker disk/read_performance_.* certification-status=blocker disk/storage_device_.* certification-status=blocker + +id: disk-full +unit: test plan +_name: Disks tests +_description: QA disk tests for Ubuntu Core devices +include: +nested_part: + disk-automated + +id: disk-automated +unit: test plan +_name: Automated disk tests +_description: Automated disk tests for Ubuntu Core devices +estimated_duration: 1h30m +include: + disk/detect + disk/stats_.* + disk/read_performance_.* + disk/storage_device_.* +bootstrap_include: + device diff --git a/units/ethernet/jobs.pxu b/units/ethernet/jobs.pxu index 90e0edf..c6e679a 100644 --- a/units/ethernet/jobs.pxu +++ b/units/ethernet/jobs.pxu @@ -129,3 +129,275 @@ requires: command: network test -i {interface} -t stress _description: Automated test that tests performance of ethernet device {__index__} ({interface}). + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' +id: ethernet/ping_{interface} +_summary: Can ping another machine over Ethernet port {interface} +_description: Check Ethernet works by pinging another machine +plugin: shell +command: + gateway_ping_test -v --interface {interface} +category_id: com.canonical.plainbox::ethernet +estimated_duration: 4.0 +flags: preserve-locale also-after-suspend + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.mac != 'UNKNOWN' +id: ethernet/wol_S5_{interface} +_summary: Wake on LAN (WOL) test from S5 - {interface} +_purpose: + Check that another system can wake up from S5 the SUT using ethernet port {interface} WOL function. +_steps: + 1. Ensure there is an ethernet cable attached to port {interface}. + 2. Press Enter for S5 (Soft Off). + 3. From another computer on the same network run the following command: + $ wakeonlan {mac} + If wakeonlan tool is not installed run: + $ sudo apt install wakeonlan + 4. Resume Checkbox +_verification: + Did the SUT wake up from S5? +plugin: user-interact-verify +command: poweroff +user: root +category_id: com.canonical.plainbox::ethernet +estimated_duration: 120 +flags: preserve-locale + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.mac != 'UNKNOWN' +id: ethernet/wol_S4_{interface} +_summary: Wake on LAN (WOL) test from S4 - {interface} +_purpose: + Check that another system can wake up from S4 the SUT using ethernet port {interface} WOL function. +_steps: + 1. Ensure there is an ethernet cable attached to port {interface}. + 2. Press Enter to hibernate the system. + 3. From another computer on the same network run the following command: + $ wakeonlan {mac} + If wakeonlan tool is not installed run: + $ sudo apt install wakeonlan +_verification: + Did the SUT wake up from S4? +plugin: user-interact-verify +requires: + sleep.disk == 'supported' +command: systemctl hibernate +user: root +category_id: com.canonical.plainbox::ethernet +estimated_duration: 120 +flags: preserve-locale + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.mac != 'UNKNOWN' and device.interface != 'UNKNOWN' +id: ethernet/wol_S3_{interface} +_summary: Wake on LAN (WOL) test from S3 - {interface} +_purpose: + Check that another system can wake up from S3 the SUT using ethernet port {interface} WOL function. +_steps: + 1. Ensure there is an ethernet cable attached to port {interface}. + 2. Press Enter to suspend the system. + 3. From another computer on the same network run the following command: + $ wakeonlan {mac} + If wakeonlan tool is not installed run: + $ sudo apt install wakeonlan +_verification: + Did the SUT wake up from S3? +plugin: user-interact-verify +requires: + sleep.mem == 'supported' +command: systemctl suspend +user: root +category_id: com.canonical.plainbox::ethernet +estimated_duration: 120 +flags: preserve-locale + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' +template-engine: jinja2 +id: ethernet/hotplug-{{ interface }}-check-disconnected +_summary: Ensure ethernet port {{ interface }} is physically disconnected +_purpose: + Check that ethernet port {{ interface }} is detected as being in the correct state + to begin a hotplug connection test. +_steps: + 1. Ensure there is no ethernet cable attached to port {{ interface }}. + 2. Begin test. +plugin: user-interact +command: + if [ $(cat /sys/class/net/{{ interface }}/carrier) -ne 0 ]; then + # carrier indicating cable present + exit 1 + fi + exit 0 +category_id: com.canonical.plainbox::ethernet +estimated_duration: 1.0 +flags: preserve-locale +after: + {%- if __index__ > 1 %}ethernet/hotplug-{{ __index__ - 1 }}-end-cycle{%- endif %} + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' +id: ethernet/hotplug-{interface}-connect +_summary: Ethernet port {interface} hotplug detection test +_purpose: + Check ethernet port {interface} connects when cable inserted. Assumes an IP + address will be assigned by DHCP. Connection asserted by pinging the network + defined gateway. +_steps: + 1. Begin the test. + 2. Insert the ethernet cable in to ethernet port {interface}. + 3. This test will timeout and fail if the insertion and connection + establishment has not been detected (10 second timeout for each check). +plugin: user-interact +command: + LOOP=0 + CABLE_DETECT=0 + while [ $LOOP -lt 10 ] && [ $CABLE_DETECT -eq 0 ] + do + sleep 1 + CABLE_DETECT=$(cat /sys/class/net/{interface}/carrier) + LOOP=$((LOOP=LOOP+1)) + done + if [ $CABLE_DETECT -eq 0 ]; then + echo "Didn't detect a cable insertion" + exit 1 + fi + echo "Detected a cable insertion" + LOOP=0 + OPSTATE="unknown" + while [ $LOOP -lt 10 ] && [ "$OPSTATE" != "up" ] + do + sleep 1 + OPSTATE=$(cat /sys/class/net/{interface}/operstate) + LOOP=$((LOOP=LOOP+1)) + done + if [ "$OPSTATE" != "up" ]; then + echo "Interface did not up" + exit 1 + fi + echo "Interface up" + gateway_ping_test -v --interface {interface} + PING_TEST=$? + if [ $PING_TEST -ne 0 ]; then + echo "Ping test failed" + exit 1 + fi + exit 0 +category_id: com.canonical.plainbox::ethernet +estimated_duration: 60.0 +depends: ethernet/hotplug-{interface}-check-disconnected + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' +id: ethernet/hotplug-{interface}-disconnect +_summary: Ethernet port {interface} hotplug disconnect step +_purpose: + Check that when cable removed from ethernet port {interface} the system + detects this correctly. +_steps: + 1. Depends on previous hotplug connection test passing. We will now test + cable disconnection. + 2. Begin the test. + 3. Remove the ethernet cable from ethernet port {interface}. + 4. This test will timeout and fail if the removal has not been detected and + interface marked as down (10 second timeout for each check). +plugin: user-interact +command: + LOOP=0 + CABLE_DETECT=1 + while [ $LOOP -lt 10 ] && [ $CABLE_DETECT -ne 0 ] + do + sleep 1 + CABLE_DETECT=$(cat /sys/class/net/{interface}/carrier) + LOOP=$((LOOP=LOOP+1)) + done + if [ $CABLE_DETECT -ne 0 ]; then + echo "Didn't detect a cable removal" + exit 1 + fi + echo "Detected a cable removal" + LOOP=0 + OPSTATE="up" + while [ $LOOP -lt 10 ] && [ "$OPSTATE" == "up" ] + do + sleep 1 + OPSTATE=$(cat /sys/class/net/{interface}/operstate) + LOOP=$((LOOP=LOOP+1)) + done + if [ "$OPSTATE" == "up" ]; then + echo "Interface did not go down" + exit 1 + fi + echo "Interface went down" + exit 0 +category_id: com.canonical.plainbox::ethernet +estimated_duration: 20.0 +depends: ethernet/hotplug-{interface}-connect + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' +id: ethernet/hotplug-{__index__}-end-cycle +after: ethernet/hotplug-{interface}-disconnect +command: true +flags: simple + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' +template-unit: job +id: ethernet/iperf3_{interface} +plugin: shell +_summary: Iperf3 stress testing for {interface} +category_id: com.canonical.plainbox::ethernet +estimated_duration: 740.0 +user: root +environ: + TEST_TARGET_IPERF + LD_LIBRARY_PATH +command: checkbox-support-network test -i {interface} -t iperf --iperf3 --scan-timeout 3600 --fail-threshold 80 --cpu-load-fail-threshold 90 --runtime 90 --num_runs 4 +_description: + This test uses iperf3 to ensure network devices pass data at an acceptable + minimum percentage of advertized speed. + +unit: template +template-resource: device +template-filter: device.category == 'NETWORK' and device.interface != 'UNKNOWN' +template-engine: jinja2 +template-unit: job +id: ethernet/check-{{ interface }}-static-configuration +_summary: Check that {{ interface }} has a static configuration +_description: + This job is intended to be used on particular devices where an interface has + been identified for configuration with a static IP address. As such, this is + not suitable for inclusion in the ethernet nested test plans, but should be + specifically included where required. +category_id: com.canonical.plainbox::ethernet +plugin: shell +estimated_duration: 2s +command: check_static {{ interface }} +requires: + snap.name == 'network-manager' + connections.slot == 'network-manager:service' and connections.plug == '{{ __system_env__["SNAP_NAME"] }}:network-manager' + +plugin: shell +category_id: com.canonical.plainbox::ethernet +id: after-suspend-ethernet/network_resume_time_auto +depends: suspend/suspend_advanced_auto +estimated_duration: 1.2 +requires: + device.category == 'NETWORK' +command: network_reconnect_resume_test -t 10 -d wired +_summary: Network reconnect resume test (wired) +_description: + Checks the length of time it takes to reconnect an existing wired connection + after a suspend/resume cycle. diff --git a/units/graphics/jobs.pxu b/units/graphics/jobs.pxu index 08bc7b3..88efe86 100644 --- a/units/graphics/jobs.pxu +++ b/units/graphics/jobs.pxu @@ -8,7 +8,7 @@ command: source graphics_env {driver} {index} graphics_driver estimated_duration: 0.5 -_description: Parses Xorg.0.Log and discovers the running X driver and version for the {vendor} {product} graphics card +_description: Parses Xorg.0.log and discovers the running X driver and version for the {vendor} {product} graphics card _summary: Test X driver/version for {vendor} {product} plugin: shell @@ -205,7 +205,7 @@ _description: plugin: shell category_id: com.canonical.plainbox::graphics id: graphics/VESA_drivers_not_in_use -command: cat /var/log/Xorg.0.log | perl -e '$a=0;while(<>){$a++ if /Loading.*vesa_drv\.so/;$a-- if /Unloading.*vesa/&&$a}exit 1 if $a' +command: cat /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log 2>&1 | perl -e '$a=0;while(<>){$a++ if /Loading.*vesa_drv\.so/;$a-- if /Unloading.*vesa/&&$a}exit 1 if $a' estimated_duration: 0.011 _description: Check that VESA drivers are not in use _summary: Test that VESA drivers are not in use diff --git a/units/mediacard/jobs.pxu b/units/mediacard/jobs.pxu index ab00396..7bbfded 100644 --- a/units/mediacard/jobs.pxu +++ b/units/mediacard/jobs.pxu @@ -1,13 +1,14 @@ plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/mmc-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' @@ -40,16 +41,17 @@ _description: test is run. It tests reading and writing to the MMC card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/mmc-remove estimated_duration: 30.0 depends: mediacard/mmc-insert command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root _description: PURPOSE: @@ -63,20 +65,22 @@ _description: automatically selected result. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/sd-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' manifest.has_card_reader == 'True' user: root +_summary: Test that insertion of an SD card is detected (udisks2) _description: PURPOSE: This test will check that the systems media card reader can @@ -99,22 +103,25 @@ flags: preserve-cwd command: removable_storage_test -s 268400000 --memorycard sdio usb scsi requires: package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) +_summary: Test reading & writing to a SD Card (udisks2) _description: This test is automated and executes after the mediacard/sd-insert test is run. It tests reading and writing to the SD card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/sd-remove estimated_duration: 30.0 depends: mediacard/sd-insert command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root +_summary: Test that removal of an SD card is detected _description: PURPOSE: This test will check that the system correctly detects @@ -138,26 +145,29 @@ requires: package.name == 'udisks2' or snap.name == 'udisks2' package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) manifest.has_card_reader == 'True' +_summary: Automated test of SD Card reading & writing (udisk2) _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. It is intended for SRU automated testing. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/sdhc-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' manifest.has_card_reader == 'True' user: root +_summary: Test that insertion of an SDHC card is detected (udisks2) _description: PURPOSE: This test will check that the systems media card reader can @@ -181,22 +191,25 @@ flags: preserve-cwd command: removable_storage_test -s 268400000 --memorycard sdio usb scsi requires: package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) +_summary: Test that insertion of an SDHC card is detected (udisks2) _description: This test is automated and executes after the mediacard/sdhc-insert test is run. It tests reading and writing to the SDHC card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/sdhc-remove estimated_duration: 30.0 depends: mediacard/sdhc-insert command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root +_summary: Test that removal of an SDHC card is detected _description: PURPOSE: This test will check that the system correctly detects @@ -209,15 +222,16 @@ _description: automatically selected result. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/cf-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' @@ -249,16 +263,17 @@ _description: test is run. It tests reading and writing to the CF card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/cf-remove depends: mediacard/cf-storage estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root _description: PURPOSE: @@ -272,20 +287,22 @@ _description: automatically selected result. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/sdxc-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' manifest.has_card_reader == 'True' user: root +_summary: Test that insertion of an SDXC card is detected (udisks2) _description: PURPOSE: This test will check that the systems media card reader can @@ -307,22 +324,25 @@ user: root command: removable_storage_test -s 268400000 --memorycard sdio usb scsi requires: package.name == 'udisks2' or (snap.name == 'core' and int(snap.revision) >= 1804) +_summary: Test reading & writing to a SDXC Card (udisks2) _description: This test is automated and executes after the mediacard/sdxc-insert test is run. It tests reading and writing to the SDXC card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/sdxc-remove estimated_duration: 30.0 depends: mediacard/sdxc-insert command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root +_summary: Test that removal of an SDXC card is detected _description: PURPOSE: This test will check that the system correctly detects @@ -335,15 +355,16 @@ _description: automatically selected result. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/ms-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' @@ -375,16 +396,17 @@ _description: test is run. It tests reading and writing to the MS card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/ms-remove estimated_duration: 30.0 depends: mediacard/ms-insert command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root _description: PURPOSE: @@ -398,15 +420,16 @@ _description: automatically selected result. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/msp-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} user: root imports: from com.canonical.plainbox import manifest requires: @@ -438,16 +461,17 @@ _description: test is run. It tests reading and writing to the MSP card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/msp-remove estimated_duration: 30.0 depends: mediacard/msp-insert command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root _description: PURPOSE: @@ -461,15 +485,16 @@ _description: automatically selected result. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/xd-insert estimated_duration: 30.0 command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kadd.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard insert sdio usb scsi - fi + {% endif -%} imports: from com.canonical.plainbox import manifest requires: package.name == 'udisks2' or snap.name == 'udisks2' @@ -501,16 +526,17 @@ _description: test is run. It tests reading and writing to the xD card. plugin: user-interact +template-engine: jinja2 category_id: com.canonical.plainbox::mediacard id: mediacard/xd-remove estimated_duration: 30.0 depends: mediacard/xd-insert command: - if [[ -v SNAP ]]; then + {%- if __on_ubuntucore__ %} timeout 20s bash -c 'grep -m 1 -oP "\Kremove.*?mmc.*?\s" <( exec journalctl -u snap.udisks2.udisksd -f -q -S -1s ); kill $!' - else + {%- else %} removable_storage_watcher --memorycard remove sdio usb scsi - fi + {% endif -%} user: root _description: PURPOSE: diff --git a/units/miscellanea/jobs.pxu b/units/miscellanea/jobs.pxu index 59e41c2..7ddcc1b 100644 --- a/units/miscellanea/jobs.pxu +++ b/units/miscellanea/jobs.pxu @@ -81,7 +81,7 @@ id: miscellanea/fwupdate estimated_duration: 1.0 depends: miscellanea/efi_boot_mode requires: - cpuinfo.platform in ("i386", "x86_64", "aarch64", "armhf") + cpuinfo.platform in ("i386", "x86_64", "aarch64", "armv7l") package.name == 'fwupdate' or executable.name == 'fwupdate' _description: Determine if EFI firmware supports update from OS. @@ -148,6 +148,22 @@ command: boot_mode_test secureboot plugin: shell category_id: com.canonical.plainbox::miscellanea estimated_duration: 0.5 +unit: template +template-resource: model_assertion +template-unit: job +requires: + executable.name == 'dumpimage' + executable.name == 'mokutil' +id: miscellanea/secure_boot_mode_{gadget} +_summary: Test that {gadget} Ubuntu Core system booted with Secure Boot active +_description: + Test to verify that the system booted with Secure Boot active. +command: + boot_mode_test_snappy.py {gadget} {kernel} + +plugin: shell +category_id: com.canonical.plainbox::miscellanea +estimated_duration: 0.5 user: root id: miscellanea/efi_pxeboot requires: @@ -161,6 +177,16 @@ command: efi-pxeboot plugin: shell category_id: com.canonical.plainbox::miscellanea +estimated_duration: 0.5 +id: miscellanea/check_prerelease +_summary: Test that system is not a pre-release version +_description: + Test to verify that the system uses production, rather + than pre-release, versions of the kernel and the OS. +command: check-prerelease + +plugin: shell +category_id: com.canonical.plainbox::miscellanea id: miscellanea/bmc_info requires: package.name == 'ipmitool' or executable.name == 'ipmitool' @@ -196,7 +222,7 @@ requires: dmi_present.state == 'supported' estimated_duration: 0.5 user: root -command: dmitest --test_versions server +command: dmitest server _description: Sanity check of DMI system identification data (for servers) _summary: diff --git a/units/monitor/jobs.pxu b/units/monitor/jobs.pxu index 6c1ac58..255d74c 100644 --- a/units/monitor/jobs.pxu +++ b/units/monitor/jobs.pxu @@ -3,6 +3,7 @@ template-resource: graphics_card template-filter: graphics_card.prime_gpu_offload == 'Off' id: monitor/{index}_vga_{product_slug} requires: display.vga == 'supported' or display.dp == 'supported' +flags: also-after-suspend-manual plugin: manual category_id: com.canonical.plainbox::monitor _purpose: @@ -18,6 +19,7 @@ template-resource: graphics_card template-filter: graphics_card.prime_gpu_offload == 'Off' id: monitor/{index}_dvi_{product_slug} requires: display.dvi == 'supported' +flags: also-after-suspend-manual plugin: manual category_id: com.canonical.plainbox::monitor _purpose: @@ -33,6 +35,7 @@ template-resource: graphics_card template-filter: graphics_card.prime_gpu_offload == 'Off' id: monitor/{index}_displayport_{product_slug} requires: display.dp == 'supported' +flags: also-after-suspend-manual plugin: manual category_id: com.canonical.plainbox::monitor _purpose: @@ -48,6 +51,7 @@ template-resource: graphics_card template-filter: graphics_card.prime_gpu_offload == 'Off' id: monitor/{index}_hdmi_{product_slug} requires: display.hdmi == 'supported' +flags: also-after-suspend-manual plugin: manual category_id: com.canonical.plainbox::monitor _purpose: @@ -93,6 +97,7 @@ template-resource: graphics_card template-filter: graphics_card.prime_gpu_offload == 'Off' id: monitor/{index}_multi-head_{product_slug} requires: dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower', 'Space-saving'] +flags: also-after-suspend-manual plugin: manual category_id: com.canonical.plainbox::monitor _purpose: @@ -109,6 +114,7 @@ _verification: unit: template template-resource: graphics_card template-filter: graphics_card.prime_gpu_offload == 'Off' +flags: also-after-suspend-manual id: monitor/{index}_powersaving_{product_slug} plugin: user-interact-verify category_id: com.canonical.plainbox::monitor @@ -146,6 +152,7 @@ template-filter: graphics_card.prime_gpu_offload == 'Off' id: monitor/{index}_thunderbolt_{product_slug} imports: from com.canonical.plainbox import manifest requires: manifest.has_thunderbolt == 'True' +flags: also-after-suspend-manual estimated_duration: 15.0 plugin: manual category_id: com.canonical.plainbox::monitor @@ -167,6 +174,7 @@ template-filter: graphics_card.prime_gpu_offload == 'Off' id: monitor/{index}_thunderbolt3_{product_slug} imports: from com.canonical.plainbox import manifest requires: manifest.has_thunderbolt3 == 'True' +flags: also-after-suspend-manual estimated_duration: 15.0 plugin: manual category_id: com.canonical.plainbox::monitor @@ -188,6 +196,7 @@ id: monitor/{index}_type-c_displayport_{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 @@ -202,3 +211,41 @@ _steps: _verification: Was the desktop displayed correctly with {vendor} {product} on the screen connected using a "USB Type-C to DisplayPort" adapter in every mode? + +id: monitor/type-c-to-hdmi +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 +_purpose: + This test will check the connection of a screen using a "USB Type-C to HDMI" adapter +_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 on the screen connected using a + "USB Type-C to HDMI" adapter in every mode? + +id: monitor/type-c-to-vga +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 +_purpose: + This test will check the connection of a screen using a "USB Type-C to VGA" adapter +_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 on the screen connected using a + "USB Type-C to VGA" adapter in every mode? diff --git a/units/monitor/test-plan.pxu b/units/monitor/test-plan.pxu index 344b304..ca90299 100644 --- a/units/monitor/test-plan.pxu +++ b/units/monitor/test-plan.pxu @@ -16,23 +16,25 @@ _name: Monitor tests (integrated GPU) (Manual) _description: Monitor tests (integrated GPU) (Manual) 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 - audio/1_playback_displayport_.* certification-status=blocker - audio/1_playback_type-c_displayport_.* 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 - thunderbolt3/daisy-chain certification-status=non-blocker - monitor/1_vga_.* certification-status=blocker - monitor/1_multi-head_.* certification-status=blocker + (after-suspend-manual-)?monitor/1_powersaving_.* certification-status=blocker + 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 bootstrap_include: graphics_card @@ -97,20 +99,22 @@ unit: test plan _name: Monitor tests (integrated GPU, certification blockers only) _description: Monitor tests (integrated GPU, certification blockers only) 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 - audio/1_playback_displayport_.* certification-status=blocker - audio/1_playback_type-c_displayport_.* 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 - audio/1_playback_thunderbolt_.* certification-status=blocker - thunderbolt/daisy-chain certification-status=blocker - monitor/1_vga_.* certification-status=blocker - monitor/1_multi-head_.* certification-status=blocker + (after-suspend-manual-)?monitor/1_powersaving_.* certification-status=blocker + 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 bootstrap_include: graphics_card diff --git a/units/networking/test-plan.pxu b/units/networking/test-plan.pxu index e9e61e2..d6a3ddb 100644 --- a/units/networking/test-plan.pxu +++ b/units/networking/test-plan.pxu @@ -13,6 +13,9 @@ _name: Networking tests (manual) _description: Networking tests (manual) include: networking/info_device.* certification-status=blocker + ethernet/hotplug-.* certification-status=blocker +bootstrap_include: + device id: networking-cert-automated unit: test plan @@ -32,3 +35,5 @@ include: networking/gateway_ping certification-status=blocker networking/info_device.* certification-status=blocker networking/ntp certification-status=blocker +bootstrap_include: + device diff --git a/units/power-management/jobs.pxu b/units/power-management/jobs.pxu index be640c3..9f8f62e 100644 --- a/units/power-management/jobs.pxu +++ b/units/power-management/jobs.pxu @@ -36,7 +36,7 @@ id: power-management/poweroff estimated_duration: 120.0 depends: power-management/fwts_wakealarm user: root -environ: PLAINBOX_SESSION_SHARE +environ: PLAINBOX_SESSION_SHARE PM_TEST_DRY_RUN requires: executable.name == 'fwts' command: pm_test --silent --checkbox-respawn-cmd $PLAINBOX_SESSION_SHARE/__respawn_checkbox poweroff --log-level=debug --log-dir=$PLAINBOX_SESSION_SHARE flags: noreturn @@ -58,7 +58,7 @@ category_id: com.canonical.plainbox::power-management id: power-management/reboot estimated_duration: 120.0 user: root -environ: PLAINBOX_SESSION_SHARE +environ: PLAINBOX_SESSION_SHARE PM_TEST_DRY_RUN requires: executable.name == 'fwts' command: pm_test --silent --checkbox-respawn-cmd $PLAINBOX_SESSION_SHARE/__respawn_checkbox reboot --log-level=debug --log-dir=$PLAINBOX_SESSION_SHARE flags: noreturn diff --git a/units/socketcan/category.pxu b/units/socketcan/category.pxu new file mode 100644 index 0000000..169d966 --- /dev/null +++ b/units/socketcan/category.pxu @@ -0,0 +1,3 @@ +unit: category +id: socketcan +_name: SocketCAN interface tests diff --git a/units/socketcan/jobs.pxu b/units/socketcan/jobs.pxu new file mode 100644 index 0000000..0bf22a1 --- /dev/null +++ b/units/socketcan/jobs.pxu @@ -0,0 +1,166 @@ + +id: socketcan/modprobe_vcan +category_id: socketcan +_summary: Create virtual CAN device +_description: + Add a virtual CAN interface for testing kernel CAN support +plugin: shell +user: root +estimated_duration: 2.0 +command: + BASH_XTRACEFD=1 + set -ex + if lsmod | grep -v "vcan" &> /dev/null ; then + modprobe vcan + fi + if ! ip link show vcan0 &> /dev/null ; then + ip link add vcan0 type vcan + fi + ip link set vcan0 up + + +id: socketcan/send_packet_local_sff_virtual +depends: socketcan/modprobe_vcan +_summary: Virtual CAN device support test (Raw, Local) +_description: + Test that the kernel supports CAN networking by sending packets to a + virtual device using a raw socket, this is only a local test as + the broadcast packet is received on the same device +category_id: socketcan +plugin: shell +estimated_duration: 2.0 +command: + socketcan_test.py vcan0 111 + + +id: socketcan/send_packet_local_eff_virtual +depends: socketcan/modprobe_vcan +_summary: Virtual CAN device support test (Raw, Local, EFF) +_description: + Test that the kernel supports CAN networking by sending packets to a + virtual device using a raw socket, this is only a local test as + the broadcast packet is received on the same device +category_id: socketcan +plugin: shell +estimated_duration: 2.0 +command: + socketcan_test.py vcan0 1F334455 --effid + + +id: socketcan/send_packet_local_fd_virtual +depends: socketcan/modprobe_vcan +_summary: Virtual CAN device support test (Raw, Local, FD) +_description: + Test that the kernel supports CAN networking by sending packets to a + virtual device using a raw socket, this is only a local test as + the broadcast packet is received on the same device +category_id: socketcan +plugin: shell +estimated_duration: 2.0 +command: + socketcan_test.py vcan0 1A --fdmode + + +unit: template +template-resource: device +template-filter: device.category == 'SOCKETCAN' +id: socketcan/send_packet_local_sff_{interface} +_summary: CAN device support test {interface} (Raw, Local) +_description: + Test a CAN device by sending packets using a raw socket, this is only a + local test as the broadcast packet is received on the same device +category_id: socketcan +plugin: shell +estimated_duration: 2.0 +imports: from com.canonical.plainbox import manifest +requires: manifest.socket_can_echo_server_running == 'False' +command: + socketcan_test.py {interface} 111 + + +unit: template +template-resource: device +template-filter: device.category == 'SOCKETCAN' +id: socketcan/send_packet_local_eff_{interface} +_summary: CAN device support test {interface} (Raw, Local, EFF) +_description: + Test a CAN device by sending packets using a raw socket, this is only a + local test as the broadcast packet is received on the same device +category_id: socketcan +plugin: shell +estimated_duration: 2.0 +imports: from com.canonical.plainbox import manifest +requires: manifest.socket_can_echo_server_running == 'False' +command: + socketcan_test.py {interface} FA123 --effid + + +unit: template +template-resource: device +template-filter: device.category == 'SOCKETCAN' +id: socketcan/send_packet_local_fd_{interface} +_summary: CAN device support test {interface} (Raw, Local, FD) +_description: + Test a CAN device by sending packets using a raw socket, this is only a + local test as the broadcast packet is received on the same device +category_id: socketcan +plugin: shell +estimated_duration: 2.0 +imports: from com.canonical.plainbox import manifest +requires: manifest.socket_can_echo_server_running == 'False' +command: + socketcan_test.py {interface} 1B --fdmode + + +unit: template +template-resource: device +template-filter: device.category == 'SOCKETCAN' +id: socketcan/send_packet_remote_sff_{interface} +_summary: CAN device support test {interface} (Raw, Remote) +_description: + Test a CAN device by sending packets using a raw socket to a remote device. + As a prerequisite the remote device should have can-echo-server installed so + as to return the predicted packet. +category_id: socketcan +plugin: shell +estimated_duration: 5.0 +imports: from com.canonical.plainbox import manifest +requires: manifest.socket_can_echo_server_running == 'True' +command: + socketcan_test.py {interface} 111 --remote + + +unit: template +template-resource: device +template-filter: device.category == 'SOCKETCAN' +id: socketcan/send_packet_remote_eff_{interface} +_summary: CAN device support test {interface} (Raw, Remote, EFF) +_description: + Test a CAN device by sending packets using a raw socket to a remote device. + As a prerequisite the remote device should have can-echo-server installed so + as to return the predicted packet. +category_id: socketcan +plugin: shell +estimated_duration: 5.0 +imports: from com.canonical.plainbox import manifest +requires: manifest.socket_can_echo_server_running == 'True' +command: + socketcan_test.py {interface} E407DB --remote --effid + + +unit: template +template-resource: device +template-filter: device.category == 'SOCKETCAN' +id: socketcan/send_packet_remote_fd_{interface} +_summary: CAN device support test {interface} (Raw, Remote, FD) +_description: + Test a CAN device by sending packets using a raw socket to a remote device. + As a prerequisite the remote device should have can-echo-server installed so + as to return the predicted packet. +category_id: socketcan +plugin: shell +estimated_duration: 5.0 +imports: from com.canonical.plainbox import manifest +requires: manifest.socket_can_echo_server_running == 'True' +command: + socketcan_test.py {interface} 19F --remote --fdmode diff --git a/units/socketcan/manifest.pxu b/units/socketcan/manifest.pxu new file mode 100644 index 0000000..bb140c4 --- /dev/null +++ b/units/socketcan/manifest.pxu @@ -0,0 +1,5 @@ + +unit: manifest entry +id: socket_can_echo_server_running +_name: SocketCAN Echo Server Running +value-type: bool diff --git a/units/socketcan/test-plan.pxu b/units/socketcan/test-plan.pxu new file mode 100644 index 0000000..c4e343e --- /dev/null +++ b/units/socketcan/test-plan.pxu @@ -0,0 +1,28 @@ + +id: socketcan-auto-remote +unit: test plan +_name: SocketCAN Tests (Automated, Remote) +_description: + SocketCAN Tests (Automated, Remote) +include: + socketcan/send_packet_remote_.* + + +id: socketcan-auto-local +unit: test plan +_name: SocketCAN Tests, (Automated, Local) +_description: + SocketCAN Tests, (Automated, Local) +include: + socketcan/send_packet_local_.* + + +id: socketcan-full +unit: test plan +_name: SocketCAN Tests +_description: + SocketCAN Tests +include: +nested_part: + socketcan-auto-remote + socketcan-auto-local diff --git a/units/stress/jobs.pxu b/units/stress/jobs.pxu index 8d25af9..e0c2e0a 100644 --- a/units/stress/jobs.pxu +++ b/units/stress/jobs.pxu @@ -76,6 +76,7 @@ requires: executable.name == 'x-terminal-emulator' flags: noreturn user: root +environ: PM_TEST_DRY_RUN command: pm_test reboot --checkbox-respawn-cmd $PLAINBOX_SESSION_SHARE/__respawn_checkbox --fwts --log-level=debug --log-dir=$PLAINBOX_SESSION_SHARE --suspends-before-reboot=30 -r 3 --silent _description: @@ -226,7 +227,7 @@ requires: executable.name == 'fwts' command: pm_test --checkbox-respawn-cmd $PLAINBOX_SESSION_SHARE/__respawn_checkbox -r 100 --silent --log-level=notset reboot --log-dir=$PLAINBOX_SESSION_SHARE flags: noreturn user: root -environ: PLAINBOX_SESSION_SHARE +environ: PLAINBOX_SESSION_SHARE PM_TEST_DRY_RUN _description: Stress reboot system (100 cycles) @@ -248,7 +249,7 @@ command: pm_test --checkbox-respawn-cmd $PLAINBOX_SESSION_SHARE/__respawn_checkb flags: noreturn estimated_duration: 2700 user: root -environ: PLAINBOX_SESSION_SHARE +environ: PLAINBOX_SESSION_SHARE PM_TEST_DRY_RUN _description: Stress reboot system (30 cycles) @@ -270,7 +271,7 @@ requires: command: pm_test --checkbox-respawn-cmd $PLAINBOX_SESSION_SHARE/__respawn_checkbox -r 100 --silent --log-level=notset poweroff --log-dir=$PLAINBOX_SESSION_SHARE flags: noreturn user: root -environ: PLAINBOX_SESSION_SHARE +environ: PLAINBOX_SESSION_SHARE PM_TEST_DRY_RUN _description: Stress poweroff system (100 cycles) @@ -293,7 +294,7 @@ command: pm_test --checkbox-respawn-cmd $PLAINBOX_SESSION_SHARE/__respawn_checkb flags: noreturn estimated_duration: 3600 user: root -environ: PLAINBOX_SESSION_SHARE +environ: PLAINBOX_SESSION_SHARE PM_TEST_DRY_RUN _description: Stress poweroff system (30 cycles) diff --git a/units/submission/jobs.pxu b/units/submission/jobs.pxu index 43defe3..9ee5a55 100644 --- a/units/submission/jobs.pxu +++ b/units/submission/jobs.pxu @@ -12,6 +12,7 @@ plugin: attachment category_id: com.canonical.plainbox::info command: udevadm info --export-db | python3 -m plainbox dev parse udevadm _description: Attaches json dumps of udev_resource +_summary: Attaches json dumps of udev_resource id: raw_devices_dmi_json plugin: attachment diff --git a/units/suspend/suspend.pxu b/units/suspend/suspend.pxu index 34d1957..1ab5829 100644 --- a/units/suspend/suspend.pxu +++ b/units/suspend/suspend.pxu @@ -3,14 +3,14 @@ category_id: com.canonical.plainbox::suspend id: suspend/network_before_suspend depends: ethernet/detect estimated_duration: 1.2 -_description: Record the current network before suspending. +_summary: Record the current network before suspending. command: set -o pipefail; gateway_ping_test | tee $PLAINBOX_SESSION_SHARE/network_before_suspend.txt plugin: shell category_id: com.canonical.plainbox::suspend id: suspend/resolution_before_suspend estimated_duration: 1.2 -_description: Record the current resolution before suspending. +_summary: Record the current resolution before suspending. command: xrandr -q |grep '*'| awk '{print $1}' > $PLAINBOX_SESSION_SHARE/resolution_before_suspend.txt unit: template @@ -21,7 +21,7 @@ category_id: com.canonical.plainbox::suspend id: suspend/{index}_resolution_before_suspend_{product_slug} depends: graphics/{index}_switch_card_{product_slug} estimated_duration: 1.2 -_description: Record the current resolution before suspending. +_summary: Record the current resolution before suspending. command: xrandr -q |grep '*'| awk '{{print $1}}' > $PLAINBOX_SESSION_SHARE/{index}_resolution_before_suspend.txt plugin: shell @@ -31,22 +31,21 @@ estimated_duration: 1.0 requires: device.category == 'AUDIO' package.name == 'alsa-base' -_description: Record mixer settings before suspending. +_summary: Record mixer settings before suspending. command: audio_settings store --file=$PLAINBOX_SESSION_SHARE/audio_settings_before_suspend plugin: shell category_id: com.canonical.plainbox::suspend id: suspend/cpu_before_suspend estimated_duration: 1.2 -_description: Verify that all the CPUs are online before suspending +_summary: Verify that all the CPUs are online before suspending command: cpuinfo_resource > $PLAINBOX_SESSION_SHARE/cpuinfo_before_suspend plugin: shell category_id: com.canonical.plainbox::suspend id: suspend/memory_before_suspend estimated_duration: 1.2 -_description: - Dumps memory info to a file for comparison after suspend test has been run +_summary: Dumps memory info to a file for comparison after suspend command: meminfo_resource > $PLAINBOX_SESSION_SHARE/meminfo_before_suspend unit: template @@ -61,7 +60,8 @@ requires: package.name == 'iperf' user: root environ: TEST_TARGET_FTP TEST_TARGET_IPERF TEST_USER TEST_PASS -command: network -i {interface} -t iperf +command: network test -i {interface} -t iperf +_summary: Before suspend iperf test ethernet {interface} _description: This test executes iperf connection performance/stability against device {__index__} ({interface}) before suspend. @@ -76,6 +76,7 @@ command: INTERFACE=`(nmcli -t -f GENERAL -m tabular dev list 2>/dev/null || nmcli -t -f GENERAL -m tabular dev show) |grep gsm |cut -d ":" -f 13` [ -z $INTERFACE ] && exit 1 network test -i $INTERFACE -t iperf +_summary: Before suspend iperf test GSM mobilebroadband _description: This test executes iperf connection performance/stability against the broadband device found on the system before suspend. @@ -90,6 +91,7 @@ command: INTERFACE=`(nmcli -t -f GENERAL -m tabular dev list 2>/dev/null || nmcli -t -f GENERAL -m tabular dev show) |grep cdma |cut -d ":" -f 13` [ -z $INTERFACE ] && exit 1 network test -i $INTERFACE -t iperf +_summary: Before suspend iperf test CDMA mobilebroadband _description: This test executes iperf connection performance/stability against the broadband device found on the system before suspend. @@ -99,12 +101,12 @@ id: suspend/bluetooth_obex_send_before_suspend estimated_duration: 10.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -118,6 +120,7 @@ command: sleep 3 fi set -o pipefail; bluetooth_test $PLAINBOX_PROVIDER_DATA/images/JPEG_Color_Image_Ubuntu.jpg $BTDEVADDR send 2>&1 | ansi_parser +_summary: Bluetooth OBEX send before suspend _description: This is an automated Bluetooth file transfer test. It sends an image to the device specified by the BTDEVADDR environment variable. @@ -127,12 +130,12 @@ id: suspend/bluetooth_obex_browse_before_suspend estimated_duration: 10.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -146,6 +149,7 @@ command: sleep 3 fi set -o pipefail; bluetooth_test $PLAINBOX_PROVIDER_DATA/images/JPEG_Color_Image_Ubuntu.jpg $BTDEVADDR browse 2>&1 | ansi_parser +_summary: Bluetooth OBEX browse before suspend _description: This is an automated Bluetooth test. It emulates browsing on a remote device specified by the BTDEVADDR environment variable. @@ -155,12 +159,12 @@ id: suspend/bluetooth_obex_get_before_suspend estimated_duration: 20.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -174,6 +178,7 @@ command: sleep 3 fi set -o pipefail; bluetooth_test $PLAINBOX_PROVIDER_DATA/images/JPEG_Color_Image_Ubuntu.jpg $BTDEVADDR get 2>&1 | ansi_parser +_summary: Bluetooth OBEX get before suspend _description: This is an automated Bluetooth test. It receives the given file from a remote host specified by the BTDEVADDR environment variable @@ -197,6 +202,7 @@ command: set -o pipefail; sleep_test -p | tee $PLAINBOX_SESSION_SHARE/2_suspend_single_times.log fi estimated_duration: 90.0 +_summary: Manual test of suspend function _description: PURPOSE: This test will check suspend and resume @@ -253,6 +259,7 @@ id: suspend/suspend_advanced_auto requires: sleep.mem == 'supported' rtc.state == 'supported' +_summary: Automated test of suspend function _description: This is the automated version of suspend/suspend_advanced. user: root @@ -276,6 +283,7 @@ command: fi set -o pipefail; checkbox-support-fwts_test -f none -l $PLAINBOX_SESSION_SHARE/{index}_hybrid_sleep_single -s s3 --s3-hybrid --s3-sleep-delay=30 --s3-device-check --s3-device-check-delay=45 --pm-method=pm-utils | tee $PLAINBOX_SESSION_SHARE/{index}_hybrid_sleep_single_times.log estimated_duration: 90.0 +_summary: This test will check hybrid sleep and resume _description: PURPOSE: This test will check hybrid sleep and resume @@ -293,16 +301,14 @@ id: suspend/suspend-single-log-check depends: suspend/suspend_advanced_auto estimated_duration: 1.2 command: [ -e $PLAINBOX_SESSION_SHARE/suspend_single.log ] && sleep_test_log_check -v s3 $PLAINBOX_SESSION_SHARE/suspend_single.log -_description: - Automated check of the suspend log to look for errors reported by fwts +_summary: Automated check of the suspend log for errors reported by fwts plugin: attachment category_id: com.canonical.plainbox::suspend id: suspend/suspend-single-log-attach depends: suspend/suspend_advanced_auto command: [ -e $PLAINBOX_SESSION_SHARE/suspend_single.log ] && cat $PLAINBOX_SESSION_SHARE/suspend_single.log -_description: - Attaches the log from the single suspend/resume test to the results +_summary: Attaches the log from the single suspend/resume test unit: template template-resource: graphics_card @@ -312,8 +318,7 @@ category_id: com.canonical.plainbox::suspend id: suspend/{index}_suspend-single-log-attach_{product_slug} depends: suspend/{index}_suspend_after_switch_to_card_{product_slug} command: [ -e $PLAINBOX_SESSION_SHARE/{index}_suspend_single.log ] && cat $PLAINBOX_SESSION_SHARE/{index}_suspend_single.log -_description: - Attaches the log from the single suspend/resume test to the results +_summary: Attaches the log from the single suspend/resume test unit: template template-resource: graphics_card @@ -323,8 +328,7 @@ id: suspend/{index}_hybrid-sleep-single-log-check depends: suspend/{index}_hybrid_sleep_{product_slug} estimated_duration: 1.2 command: [ -e $PLAINBOX_SESSION_SHARE/{index}_hybrid_sleep_single.log ] && sleep_test_log_check -v s3 $PLAINBOX_SESSION_SHARE/{index}_hybrid_sleep_single.log -_description: - Automated check of the hybrid sleep log to look for errors reported by fwts +_summary: Automated check of the hybrid sleep log for errors reported by fwts unit: template template-resource: graphics_card @@ -333,14 +337,14 @@ category_id: com.canonical.plainbox::suspend id: suspend/{index}_hybrid-sleep-single-log-attach_{product_slug} depends: suspend/{index}_hybrid_sleep_{product_slug} command: [ -e $PLAINBOX_SESSION_SHARE/{index}_hybrid_sleep_single.log ] && cat $PLAINBOX_SESSION_SHARE/{index}_hybrid_sleep_single.log -_description: - Attaches the log from the single hybrid sleep/resume test to the results +_summary: Attaches the log from the single hybrid sleep/resume test plugin: shell category_id: com.canonical.plainbox::suspend id: suspend/suspend-time-check estimated_duration: 1.2 command: [ -e $PLAINBOX_SESSION_SHARE/suspend_single_times.log ] && sleep_time_check $PLAINBOX_SESSION_SHARE/suspend_single_times.log +_summary: Ensure time to suspend/resume is under threshold _description: Checks the sleep times to ensure that a machine suspends and resumes within a given threshold @@ -353,6 +357,7 @@ id: suspend/{index}_suspend-time-check_{product_slug} depends: suspend/{index}_suspend_after_switch_to_card_{product_slug} estimated_duration: 1.2 command: [ -e $PLAINBOX_SESSION_SHARE/{index}_suspend_single_times.log ] && sleep_time_check $PLAINBOX_SESSION_SHARE/{index}_suspend_single_times.log +_summary: Ensure time to suspend/resume is under threshold _description: Checks the sleep times to ensure that a machine suspends and resumes within a given threshold plugin: user-interact-verify @@ -615,7 +620,7 @@ requires: package.name == 'iperf' user: root environ: TEST_TARGET_FTP TEST_TARGET_IPERF TEST_USER TEST_PASS -command: network -i {interface} -t iperf +command: network test -i {interface} -t iperf _description: This test executes iperf connection performance/stability against device {__index__} ({interface}) after suspend. @@ -631,7 +636,7 @@ requires: package.name == 'iperf' user: root environ: TEST_TARGET_FTP TEST_TARGET_IPERF TEST_USER TEST_PASS -command: network -i {interface} -t iperf +command: network test -i {interface} -t iperf _description: This test executes iperf connection performance/stability against device {__index__} ({interface}) after suspend. @@ -936,12 +941,12 @@ depends: suspend/suspend_advanced estimated_duration: 10.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -965,12 +970,12 @@ depends: suspend/suspend_advanced_auto estimated_duration: 10.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -994,12 +999,12 @@ depends: suspend/suspend_advanced estimated_duration: 10.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -1023,12 +1028,12 @@ depends: suspend/suspend_advanced_auto estimated_duration: 20.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -1052,12 +1057,12 @@ estimated_duration: 20.0 depends: suspend/suspend_advanced requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' @@ -1081,12 +1086,12 @@ depends: suspend/suspend_advanced_auto estimated_duration: 20.0 requires: package.name == 'bluez' - package.name == 'obexftp' + executable.name == 'obexftp' device.category == 'BLUETOOTH' command: if [ -z "$BTDEVADDR" ] then - echo "btdevaddr option not set to device address of Bluetooth target in checkbox.ini" + echo "btdevaddr option not set to device address of Bluetooth target in checkbox.conf" exit 1 fi if rfkill list bluetooth | grep -q 'Hard blocked: yes' diff --git a/units/thunderbolt/jobs.pxu b/units/thunderbolt/jobs.pxu index 188b2f6..14fa3f8 100644 --- a/units/thunderbolt/jobs.pxu +++ b/units/thunderbolt/jobs.pxu @@ -57,6 +57,7 @@ category_id: com.canonical.plainbox::disk id: thunderbolt/daisy-chain imports: from com.canonical.plainbox import manifest requires: manifest.has_thunderbolt == 'True' +flags: also-after-suspend-manual estimated_duration: 45.0 command: removable_storage_test -s 268400000 scsi _summary: Daisy-chain testing for Thunderbolt storage and display device @@ -133,6 +134,7 @@ category_id: com.canonical.plainbox::disk id: thunderbolt3/daisy-chain imports: from com.canonical.plainbox import manifest requires: manifest.has_thunderbolt3 == 'True' +flags: also-after-suspend-manual estimated_duration: 45.0 command: removable_storage_test -s 268400000 scsi _summary: Daisy-chain testing for Thunderbolt 3 storage and display device diff --git a/units/thunderbolt/test-plan.pxu b/units/thunderbolt/test-plan.pxu index d5dc001..a0efcda 100644 --- a/units/thunderbolt/test-plan.pxu +++ b/units/thunderbolt/test-plan.pxu @@ -14,12 +14,12 @@ _name: Thunderbolt tests (Manual) _description: Thunderbolt tests (Manual) include: - 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 + 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 id: thunderbolt-cert-automated unit: test plan @@ -34,6 +34,6 @@ unit: test plan _name: Thunderbolt tests (certification blockers only) _description: Thunderbolt tests (certification blockers only) include: - thunderbolt/insert certification-status=blocker - thunderbolt/storage-test certification-status=blocker - thunderbolt/remove certification-status=blocker + thunderbolt3/insert certification-status=blocker + thunderbolt3/storage-test certification-status=blocker + thunderbolt3/remove certification-status=blocker diff --git a/units/usb/test-plan.pxu b/units/usb/test-plan.pxu index 27a0616..63c62b8 100644 --- a/units/usb/test-plan.pxu +++ b/units/usb/test-plan.pxu @@ -69,6 +69,7 @@ include: usb-c/insert certification-status=blocker usb-c/storage-automated certification-status=blocker usb-c/remove certification-status=blocker + usb-c/c-to-ethernet-adapter-insert id: after-suspend-usb-cert-full unit: test plan diff --git a/units/usb/usb-c.pxu b/units/usb/usb-c.pxu index d2004c2..cf7c5f6 100644 --- a/units/usb/usb-c.pxu +++ b/units/usb/usb-c.pxu @@ -187,3 +187,19 @@ requires: manifest.has_usb_type_c == 'True' estimated_duration: 30 +id: usb-c/c-to-ethernet-adapter-insert +plugin: user-interact +category_id: com.canonical.plainbox::usb +imports: from com.canonical.plainbox import manifest +requires: manifest.has_usb_type_c == 'True' +command: + net_if_watcher.py +_summary: Check if USB Type-C to Ethernet adapter works +_purpose: + This test will check if system detects network interface of the Type-C to + ethernet adapter. +_steps: + 1. Prepare USB Type-C to Ethernet adapter + 2. Start the test + 3. When the message "INSERT NOW" is shown, plug in the adapter to Type-C port +estimated_duration: 20 diff --git a/units/wireless/jobs.pxu b/units/wireless/jobs.pxu index 363984a..52b2b01 100644 --- a/units/wireless/jobs.pxu +++ b/units/wireless/jobs.pxu @@ -389,7 +389,7 @@ plugin: shell category_id: com.canonical.plainbox::wireless id: wireless/monitor_wireless_connection requires: - package.name == 'iperf' + executable.name == 'iperf' device.category == 'WIRELESS' user: root environ: WPA_BG_SSID WPA_BG_PSK SERVER_IPERF @@ -418,7 +418,7 @@ plugin: shell category_id: com.canonical.plainbox::wireless id: wireless/monitor_wireless_connection_udp requires: - package.name == 'iperf' + executable.name == 'iperf' device.category == 'WIRELESS' user: root environ: WPA_BG_SSID WPA_BG_PSK SERVER_IPERF |