summaryrefslogtreecommitdiff
diff options
authorJenkins <jenkins@cert-jenkins-master-201506-18558.maas>2022-06-16 08:23:24 +0000
committerJenkins <jenkins@cert-jenkins-master-201506-18558.maas>2022-06-16 08:23:24 +0000
commitffede27215f97a474c6c37d2236a9315acc58605 (patch)
treed0ddc8da2deacba9b98b188ca1eb20e58d8ffedd
parentc1d9cada2c76f18581940937aedb7409a758b8e0 (diff)
Import plainbox-provider-checkbox_0.65.0~rc1.orig.tar.gzupstream-0.65.0_rc1patched-0.65.0_rc1-1
-rwxr-xr-xbin/graphics_max_resolution.py148
-rwxr-xr-xbin/graphics_modes_info.py74
-rwxr-xr-xbin/install-method-check.sh149
-rwxr-xr-xbin/maas-version-check.sh75
-rwxr-xr-xbin/removable_storage_watcher.py45
-rwxr-xr-xbin/screenoff.sh1
-rwxr-xr-xbin/snap_tests.py5
-rwxr-xr-xbin/touchpad_test.py72
-rwxr-xr-xbin/virtualization.py371
-rwxr-xr-xbin/wifi_nmcli_test.py4
-rwxr-xr-xmanage.py2
-rw-r--r--units/graphics/jobs.pxu18
-rw-r--r--units/graphics/legacy.pxu8
-rw-r--r--units/graphics/test-plan.pxu6
-rw-r--r--units/miscellanea/jobs.pxu10
-rw-r--r--units/monitor/jobs.pxu10
-rw-r--r--units/touchpad/jobs.pxu34
-rw-r--r--units/touchpad/test-plan.pxu12
-rw-r--r--units/virtualization/jobs.pxu21
-rw-r--r--units/virtualization/test-plan.pxu4
-rw-r--r--units/zapper/jobs.pxu38
-rw-r--r--units/zapper/resource.pxu11
-rw-r--r--units/zapper/test-plan.pxu6
23 files changed, 685 insertions, 439 deletions
diff --git a/bin/graphics_max_resolution.py b/bin/graphics_max_resolution.py
new file mode 100755
index 0000000..56628a4
--- /dev/null
+++ b/bin/graphics_max_resolution.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+#
+# This file is part of Checkbox.
+#
+# Copyright 2022 Canonical Ltd.
+#
+# Authors:
+# Pierre Equoy <pierre.equoy@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 gi
+from glob import glob
+import os
+import sys
+from pathlib import Path
+
+gi.require_versions({"Gtk": "3.0", "Gdk": "3.0"})
+from gi.repository import Gdk, Gtk # noqa: E402
+
+
+def get_sysfs_info():
+ """
+ Go through each graphics cards sysfs entries to find max resolution if
+ connected to a monitor.
+ Return a list of ports with information about them.
+ """
+ ports = glob("/sys/class/drm/card*-*")
+ entries = []
+ for p in ports:
+ with open(Path(p) / "modes") as f:
+ # Topmost line in the modes file is the max resolution
+ max_resolution = f.readline().strip()
+ if max_resolution:
+ # e.g. "/sys/class/drm/card0-HDMI-A-1"
+ port = p.split("/")[-1]
+ width, height = max_resolution.split("x")
+ with open(Path(p) / "enabled") as f:
+ enabled = f.readline().strip()
+ with open(Path(p) / "dpms") as f:
+ dpms = f.readline().strip()
+ with open(Path(p) / "status") as f:
+ status = f.readline().strip()
+ port_info = {
+ "port": port,
+ "width": int(width),
+ "height": int(height),
+ "enabled": enabled, # "enabled" or "disabled"
+ "status": status, # "connected" or "disconnected"
+ "dpms": dpms, # "On" or "Off"
+ }
+ entries.append(port_info)
+ return entries
+
+
+def get_monitors_info():
+ """
+ Get information (model, manufacturer, resolution) from each connected
+ monitors using Gtk.
+ Return a list of monitors with their information.
+ """
+ Gtk.init()
+ display = Gdk.Display.get_default()
+ monitors = []
+ for i in range(display.get_n_monitors()):
+ mon = display.get_monitor(i)
+ monitor = {
+ "model": mon.get_model(),
+ "manufacturer": mon.get_manufacturer(),
+ "width": mon.get_geometry().width,
+ "height": mon.get_geometry().height,
+ "scale_factor": mon.get_scale_factor(),
+ }
+ monitors.append(monitor)
+ return monitors
+
+
+if __name__ == "__main__":
+ sysfs_entries = get_sysfs_info()
+ mons_entries = get_monitors_info()
+ total_sysfs_res = 0
+ total_mons_res = 0
+ compositor = os.environ.get("XDG_SESSION_TYPE")
+ print("Current compositor: {}".format(compositor))
+ print()
+ print("Maximum resolution found for each connected monitors:")
+ for p in sysfs_entries:
+ port = p["port"]
+ width = p["width"]
+ height = p["height"]
+ enabled = p["enabled"]
+ status = p["status"]
+ dpms = p["dpms"].lower()
+ print(
+ "\t{}: {}x{} ({}, {}, {})".format(
+ port, width, height, dpms, status, enabled
+ )
+ )
+ # If the monitor is disabled (e.g. "Single Display" mode), don't take
+ # its surface into account.
+ if enabled == "enabled":
+ total_sysfs_res += width * height
+ print()
+ print("Current resolution found for each connected monitors:")
+ for m in mons_entries:
+ model = m["model"]
+ manufacturer = m["manufacturer"]
+ scale = m["scale_factor"]
+ # Under X11, the returned width and height are in "application pixels",
+ # not "device pixels", so it has to be multiplied by the scale factor.
+ # However, Wayland always returns the "device pixels" width and height.
+ #
+ # Example: a 3840x2160 screen set to 200% scale will have
+ # width = 1920, height = 1080, scale_factor = 2 on X11
+ # width = 3840, height = 2160, scale_factor = 2 on Wayland
+ if compositor == "x11":
+ width = m["width"] * m["scale_factor"]
+ height = m["height"] * m["scale_factor"]
+ else:
+ width = m["width"]
+ height = m["height"]
+ print(
+ "\t{} ({}): {}x{} @{}%".format(
+ model, manufacturer, width, height, scale * 100
+ )
+ )
+ total_mons_res += width * height
+ print()
+ if total_sysfs_res == total_mons_res:
+ print("The displays are configured at their maximum resolution.")
+ else:
+ sys.exit(
+ (
+ "The displays do not seem to be configured at their maximum "
+ "resolution.\nPlease switch to the maximum resolution before "
+ "continuing."
+ )
+ )
diff --git a/bin/graphics_modes_info.py b/bin/graphics_modes_info.py
deleted file mode 100755
index f92cd76..0000000
--- a/bin/graphics_modes_info.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-#
-# graphics_modes_info.py
-#
-# This file is part of Checkbox.
-#
-# Copyright 2012 Canonical Ltd.
-#
-# Authors: Alberto Milone <alberto.milone@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/>.
-
-from __future__ import print_function
-from __future__ import unicode_literals
-import sys
-
-from checkbox_support.contrib import xrandr
-
-
-def print_modes_info(screen):
- """Print some information about the detected screen and its outputs"""
- xrandr._check_required_version((1, 0))
- print("Screen %s: minimum %s x %s, current %s x %s, maximum %s x %s" %
- (screen._screen,
- screen._width_min, screen._height_min,
- screen._width, screen._height,
- screen._width_max, screen._height_max))
- print(" %smm x %smm" % (screen._width_mm, screen._height_mm))
- print("Outputs:")
- for o in list(screen.outputs.keys()):
- output = screen.outputs[o]
- print(" %s" % o, end=' ')
- if output.is_connected():
- print("(%smm x %smm)" % (output.get_physical_width(),
- output.get_physical_height()))
- modes = output.get_available_modes()
- print(" Modes:")
- for m in range(len(modes)):
- mode = modes[m]
- refresh = mode.dotClock / (mode.hTotal * mode.vTotal)
- print(
- " [%s] %s x %s @ %s Hz" %
- (m, mode.width, mode.height, refresh), end=' ')
- if mode.id == output._mode:
- print("(current)", end=' ')
- if m == output.get_preferred_mode():
- print("(preferred)", end=' ')
- print("")
- else:
- print("(not connected)")
-
-
-def main():
- screen = xrandr.get_current_screen()
- try:
- print_modes_info(screen)
- except(xrandr.UnsupportedRRError):
- print('Error: RandR version lower than 1.0', file=sys.stderr)
-
-
-if __name__ == '__main__':
- main()
diff --git a/bin/install-method-check.sh b/bin/install-method-check.sh
new file mode 100755
index 0000000..20c0e61
--- /dev/null
+++ b/bin/install-method-check.sh
@@ -0,0 +1,149 @@
+#!/bin/bash
+
+# Copyright (C) 2012-2022 Canonical Ltd.
+
+# Authors
+# Jeff Lane <jeff@ubuntu.com>
+# 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/>.
+
+MAAS_FILE="/etc/installed-by-maas"
+DATASOURCE_FILE="/var/lib/cloud/instance/datasource"
+INSTALL_DATASOURCE=""
+SUBIQUITY_LOG_FILE1="/var/log/installer/subiquity-info.log" # Observed on 20.04
+SUBIQUITY_LOG_FILE2="/var/log/installer/subiquity-server-info.log" # Observed on 22.04
+CONFIRM_INSTALL_TYPE="maas" # or "subiquity", maybe others in future
+RETVAL="0"
+
+get_params() {
+ # Default to equivalent of --maas; the option is provided for clarity
+ # in calling script.
+ while [[ $# -gt 0 ]]; do
+ case $1 in
+ --maas|-m)
+ CONFIRM_INSTALL_TYPE="maas" # should already be set
+ ;;
+ --iso|-i)
+ CONFIRM_INSTALL_TYPE="subiquity"
+ ;;
+ *) echo "Usage: $0 [ -m, --maas ] | [ -i, --iso ]"
+ ;;
+ esac
+ shift
+ done
+ INSTALL_DATASOURCE_FOUND=0 # Should be found for both MAAS & subiquity
+ MAAS_IP_FOUND=0 # Should be found for MAAS but not subiquity
+ MAAS_VERSION_FOUND=0 # Should be found for MAAS but not subiquity
+ SUBIQUITY_LOG_FOUND=0 # Should be found for subiquity but not MAAS
+}
+
+# Context-sensitive echo() function; prints the message only if the program
+# is launched to confirm a MAAS installation
+conditional_print() {
+ if [[ "$CONFIRM_INSTALL_TYPE" == "$2" ]] ; then
+ echo "$1"
+ fi
+}
+
+# Find the installation data source, as recorded in cloud installer files.
+# This should be present for both MAAS and subiquity installs.
+get_install_datasource() {
+ # Is the file there?
+ if [ -s $DATASOURCE_FILE ]; then
+ INSTALL_DATASOURCE=$(cut -d "[" -f 2 $DATASOURCE_FILE | cut -d "]" -f 1)
+ echo "Installation data source is $INSTALL_DATASOURCE"
+ INSTALL_DATASOURCE_FOUND=1
+ else
+ echo "ERROR: The installation data source file ($DATASOURCE_FILE)"
+ echo "cannot be found."
+ fi
+}
+
+# Verify that the $INSTALL_DATASOURCE points to a valid IP address.
+# Note: Function assumes that $INSTALL_DATASOURCE is already set, as is
+# done by the get_install_datasource() function.
+verify_maas_ip() {
+ if [[ $INSTALL_DATASOURCE_FOUND == 1 ]]; then
+ MAAS_HOSTNAME=$(echo "$INSTALL_DATASOURCE" | cut -d "/" -f 3 | cut -d ":" -f 1)
+ HOST_OUTPUT=$(host "$MAAS_HOSTNAME" | grep "has address")
+ status=$?
+ if [[ $status -eq 0 ]]; then
+ MAAS_IP=$(echo "$HOST_OUTPUT" | cut -d " " -f 4)
+ conditional_print "MAAS server's IP address is $MAAS_IP" "maas"
+ conditional_print "ERROR: MAAS server's IP address is $MAAS_IP" "subiquity"
+ MAAS_IP_FOUND=1
+ else
+ conditional_print "ERROR: Unable to determine MAAS server's IP address" "maas"
+ fi
+ fi
+}
+
+# Pull the MAAS version information from a file left here by the
+# Server Certification pre-seed file
+get_maas_version() {
+ # Is the file there?
+ if [ -s $MAAS_FILE ]; then
+ maas_version=$(cat $MAAS_FILE)
+ conditional_print "MAAS version is $maas_version" "maas"
+ conditional_print "ERROR: Server Certification MAAS version file found; MAAS version is $maas_version" "subiquity"
+ MAAS_VERSION_FOUND=1
+ else
+ conditional_print "ERROR: The MAAS version cannot be determined" "maas"
+ fi
+}
+
+find_subiquity_log() {
+ if [[ -f "$SUBIQUITY_LOG_FILE1" || -f "$SUBIQUITY_LOG_FILE2" ]]; then
+ conditional_print "ERROR: Subiquity log file found" "maas"
+ conditional_print "subiquity log file found" "subiquity"
+ SUBIQUITY_LOG_FOUND=1
+ else
+ conditional_print "ERROR: Subiquity log file not found" "subiquity"
+ fi
+}
+
+#######################
+#
+# Main program begins here....
+#
+#######################
+
+get_params "$@"
+
+# Check for various installation signatures....
+get_install_datasource
+verify_maas_ip
+get_maas_version
+find_subiquity_log
+
+MAAS_FOUND=$((INSTALL_DATASOURCE_FOUND && MAAS_IP_FOUND && MAAS_VERSION_FOUND))
+SUBIQUITY_FOUND=$((INSTALL_DATASOURCE_FOUND && SUBIQUITY_LOG_FOUND))
+
+if [[ $CONFIRM_INSTALL_TYPE == "maas" ]] ; then
+ RETVAL=$((! MAAS_FOUND)) || SUBIQUITY_FOUND
+ if [[ $RETVAL == 0 ]] ; then
+ echo "PASS: System appears to have been installed by MANIACS-compliant MAAS."
+ else
+ echo "FAIL: System appears to have not been installed by MANIACS-compliant MAAS."
+ fi
+elif [[ $CONFIRM_INSTALL_TYPE == "subiquity" ]] ; then
+ RETVAL=$((! SUBIQUITY_FOUND)) || MAAS_FOUND
+ if [[ $RETVAL == 0 ]] ; then
+ echo "PASS: System appears to have been installed by a Subiquity ISO."
+ else
+ echo "FAIL: System appears to have not been installed by a Subiquity ISO."
+ fi
+fi
+
+exit $RETVAL
diff --git a/bin/maas-version-check.sh b/bin/maas-version-check.sh
deleted file mode 100755
index 7d97f0b..0000000
--- a/bin/maas-version-check.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/bash
-
-# Copyright (C) 2012-2022 Canonical Ltd.
-
-# Authors
-# Jeff Lane <jeff@ubuntu.com>
-# 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/>.
-
-MAAS_FILE="/etc/installed-by-maas"
-DATASOURCE_FILE="/var/lib/cloud/instance/datasource"
-MAAS_DATASOURCE=""
-RETVAL="0"
-
-# Find the MAAS data source, as recorded in cloud installer files
-get_maas_datasource() {
- # Is the file there?
- if [ -s $DATASOURCE_FILE ]; then
- MAAS_DATASOURCE=$(cut -d "[" -f 2 $DATASOURCE_FILE | cut -d "]" -f 1)
- echo "MAAS data source is $MAAS_DATASOURCE"
- else
- echo "ERROR: This system does not appear to have been installed by MAAS"
- echo "ERROR: " "$(ls -l $DATASOURCE_FILE 2>&1)"
- RETVAL="1"
- fi
-}
-
-# Verify that the $MAAS_DATASOURCE points to a valid IP address.
-# Note: Function assumes that $MAAS_DATASOURCE is already set, as is
-# done by the get_maas_datasource() function.
-verify_maas_ip() {
- if [[ $RETVAL == 0 ]]; then
- MAAS_HOSTNAME=$(echo "$MAAS_DATASOURCE" | cut -d "/" -f 3 | cut -d ":" -f 1)
- HOST_OUTPUT=$(host "$MAAS_HOSTNAME" | grep "has address")
- status=$?
- if [[ $status -eq 0 ]]; then
- MAAS_IP=$(echo "$HOST_OUTPUT" | cut -d " " -f 4)
- echo "MAAS server's IP address is $MAAS_IP"
- else
- echo "ERROR: Unable to determine MAAS server's IP address"
- RETVAL=1
- fi
- fi
-}
-
-# Pull the MAAS version information from a file left here by the
-# Server Certification pre-seed file
-get_maas_version() {
- # Is the file there?
- if [ -s $MAAS_FILE ]; then
- maas_version=$(cat $MAAS_FILE)
- echo "MAAS version is $maas_version"
- else
- echo "ERROR: The MAAS version cannot be determined"
- echo "ERROR: " "$(ls -l $MAAS_FILE 2>&1)"
- RETVAL="1"
- fi
-}
-
-get_maas_datasource
-verify_maas_ip
-get_maas_version
-
-exit $RETVAL
diff --git a/bin/removable_storage_watcher.py b/bin/removable_storage_watcher.py
index c13439e..7208954 100755
--- a/bin/removable_storage_watcher.py
+++ b/bin/removable_storage_watcher.py
@@ -5,7 +5,9 @@ import collections
import copy
import dbus
import logging
+import os
import sys
+import threading
import gi
gi.require_version('GUdev', '1.0')
@@ -16,12 +18,14 @@ from checkbox_support.dbus.udisks2 import UDisks2Model # noqa: E402
from checkbox_support.dbus.udisks2 import UDisks2Observer # noqa: E402
from checkbox_support.dbus.udisks2 import is_udisks2_supported # noqa: E402
from checkbox_support.dbus.udisks2 import lookup_udev_device # noqa: E402
-from checkbox_support.dbus.udisks2 import (
+from checkbox_support.dbus.udisks2 import ( # noqa: E402
map_udisks1_connection_bus) # noqa: E402
from checkbox_support.heuristics.udisks2 import is_memory_card # noqa: E402
from checkbox_support.parsers.udevadm import CARD_READER_RE # noqa: E402
from checkbox_support.parsers.udevadm import GENERIC_RE # noqa: E402
from checkbox_support.parsers.udevadm import FLASH_RE # noqa: E402
+from checkbox_support.scripts.zapper_proxy import ( # noqa: E402
+ ControlVersionDecider)
from checkbox_support.udev import get_interconnect_speed # noqa: E402
from checkbox_support.udev import get_udev_block_devices # noqa: E402
@@ -877,6 +881,8 @@ def main():
dest='logging_level', help="Enable debugging")
parser.add_argument('--unmounted', action='store_true',
help="Don't require drive being automounted")
+ parser.add_argument('--zapper-usb-address', type=str,
+ help="Zapper's USB switch address to use")
parser.set_defaults(logging_level=logging.WARNING)
args = parser.parse_args()
@@ -910,11 +916,38 @@ def main():
args.action, args.device, args.minimum_speed, args.memorycard)
# Run the actual listener and wait till it either times out of discovers
# the appropriate media changes
- print("\n\n{} NOW\n\n".format(args.action.upper()), flush=True)
- try:
- return listener.check(args.timeout)
- except KeyboardInterrupt:
- return 1
+ if args.zapper_usb_address:
+ zapper_host = os.environ.get('ZAPPER_ADDRESS')
+ if not zapper_host:
+ raise SystemExit(
+ "ZAPPER_ADDRESS environment variable not found!")
+ zapper_control = ControlVersionDecider().decide(zapper_host)
+ usb_address = args.zapper_usb_address
+ delay = 5 # in seconds
+
+ def do_the_insert():
+ logging.info("Calling zapper to connect the USB device")
+ zapper_control.usb_set_state(usb_address, 'dut')
+ insert_timer = threading.Timer(delay, do_the_insert)
+
+ def do_the_remove():
+ logging.info("Calling zapper to disconnect the USB device")
+ zapper_control.usb_set_state(usb_address, 'off')
+ remove_timer = threading.Timer(delay, do_the_remove)
+ if args.action == "insert":
+ logging.info("Starting timer for delayed insertion")
+ insert_timer.start()
+ elif args.action == "remove":
+ logging.info("Starting timer for delayed removal")
+ remove_timer.start()
+ try:
+ res = listener.check(args.timeout)
+ return res
+ except KeyboardInterrupt:
+ return 1
+
+ else:
+ print("\n\n{} NOW\n\n".format(args.action.upper()), flush=True)
if __name__ == "__main__":
diff --git a/bin/screenoff.sh b/bin/screenoff.sh
index 0769e58..7c07f7d 100755
--- a/bin/screenoff.sh
+++ b/bin/screenoff.sh
@@ -34,3 +34,4 @@ evemu-event "${device}" --type EV_MSC --code MSC_SCAN --value 28
evemu-event --sync "${device}" --type EV_KEY --code KEY_ENTER --value 1
evemu-event "${device}" --type EV_MSC --code MSC_SCAN --value 28
evemu-event --sync "${device}" --type EV_KEY --code KEY_ENTER --value 0
+loginctl unlock-session
diff --git a/bin/snap_tests.py b/bin/snap_tests.py
index 1cc84bf..b20cd1c 100755
--- a/bin/snap_tests.py
+++ b/bin/snap_tests.py
@@ -22,6 +22,8 @@ except KeyError:
TEST_SNAP = 'test-snapd-tools-core18'
elif 'checkbox20' in runtime:
TEST_SNAP = 'test-snapd-tools-core20'
+ elif 'checkbox22' in runtime:
+ TEST_SNAP = 'test-snapd-tools-core22'
else:
TEST_SNAP = 'test-snapd-tools'
SNAPD_TASK_TIMEOUT = int(os.getenv('SNAPD_TASK_TIMEOUT', 30))
@@ -36,7 +38,8 @@ class SnapList():
"""snap list should show the core package is installed."""
data = Snapd().list()
for snap in data:
- if snap['name'] in ('core', 'core16', 'core18', 'core20'):
+ if snap['name'] in ('core', 'core16', 'core18', 'core20',
+ 'core22'):
print("Found a core snap")
print(snap['name'], snap['version'], snap['revision'])
return 0
diff --git a/bin/touchpad_test.py b/bin/touchpad_test.py
index 6ecde92..83940ec 100755
--- a/bin/touchpad_test.py
+++ b/bin/touchpad_test.py
@@ -3,6 +3,7 @@
import gi
import sys
import gettext
+from time import sleep
from gettext import gettext as _
gi.require_version('Gdk', '3.0')
@@ -43,7 +44,7 @@ class GtkScroller(object):
# Initialize GTK constants
self.ICON_SIZE = Gtk.IconSize.BUTTON
self.ICON_TESTED = Gtk.STOCK_YES
- self.ICON_UNTESTED = Gtk.STOCK_INDEX
+ self.ICON_UNTESTED = Gtk.STOCK_DIALOG_QUESTION
self.ICON_NOT_REQUIRED = Gtk.STOCK_REMOVE
self.button_factory = Gtk.Button
@@ -70,7 +71,7 @@ class GtkScroller(object):
button_hbox = self._add_hbox(vbox)
validation_hbox = self._add_hbox(vbox)
self.status = self._add_label(vbox)
- self.exit_button = self._add_button(vbox, Gtk.STOCK_CLOSE)
+ self.exit_button = self._add_button(vbox, "_Close")
self.exit_button.connect("clicked", lambda w: self.quit())
# Add widgets for each direction.
@@ -78,15 +79,15 @@ class GtkScroller(object):
for direction in self.directions:
self._add_label(button_hbox, direction.name)
self.icons[direction] = self._add_image(
- validation_hbox, Gtk.STOCK_INDEX)
+ validation_hbox, self.ICON_UNTESTED)
self.show_text(
_("Please move the mouse cursor to this window.") +
"\n" +
_("Then scroll in each direction on your touchpad."))
- def _add_button(self, context, stock):
- button = self.button_factory(stock=stock)
+ def _add_button(self, context, label):
+ button = self.button_factory.new_with_mnemonic(label)
context.add(button)
button.show()
return button
@@ -124,29 +125,33 @@ class GtkScroller(object):
def run(self):
# Save touchpad settings.
- if self.horiz_scroll_key:
- self.saved_horiz_scroll_enabled = \
- self.touchpad_settings.get_boolean("horiz-scroll-enabled")
- self.saved_scroll_method = self.touchpad_settings.get_string(
- "scroll-method")
+ self.saved_edge_scrolling_enabled = self.touchpad_settings.get_boolean(
+ "edge-scrolling-enabled")
+ self.saved_two_finger_enabled = self.touchpad_settings.get_boolean(
+ "two-finger-scrolling-enabled")
# Set touchpad settings.
- if self.horiz_scroll_key:
- self.touchpad_settings.set_boolean("horiz-scroll-enabled", True)
if self.edge_scroll:
- self.touchpad_settings.set_string(
- "scroll-method", "edge-scrolling")
-
+ self.touchpad_settings.set_boolean(
+ "edge-scrolling-enabled", True)
+ self.touchpad_settings.set_boolean(
+ "two-finger-scrolling-enabled", False)
+ else:
+ self.touchpad_settings.set_boolean(
+ "two-finger-scrolling-enabled", True)
+ self.touchpad_settings.set_boolean(
+ "edge-scrolling-enabled", False)
Gtk.main()
def quit(self):
# Reset touchpad settings.
- if self.horiz_scroll_key:
- self.touchpad_settings.set_boolean(
- "horiz-scroll-enabled", self.saved_horiz_scroll_enabled)
- self.touchpad_settings.set_string(
- "scroll-method", self.saved_scroll_method)
-
+ self.touchpad_settings.set_boolean(
+ "two-finger-scrolling-enabled", self.saved_two_finger_enabled)
+ # GNOME does not like when both settings are set at the same time, so
+ # waiting a bit.
+ sleep(0.1)
+ self.touchpad_settings.set_boolean(
+ "edge-scrolling-enabled", self.saved_edge_scrolling_enabled)
Gtk.main_quit()
def show_text(self, text, widget=None):
@@ -156,7 +161,7 @@ class GtkScroller(object):
def found_direction(self, direction):
direction.tested = True
- self.icons[direction].set_from_stock(
+ self.icons[direction].set_from_icon_name(
self.ICON_TESTED, size=self.ICON_SIZE)
self.check_directions()
@@ -169,10 +174,25 @@ class GtkScroller(object):
def on_scroll(self, window, event):
for direction in self.directions:
- if direction.value == event.direction:
- self.found_direction(direction)
- break
-
+ scroll_delta, delta_x, delta_y = event.get_scroll_deltas()
+ if scroll_delta:
+ event_direction = None
+ # Arbitrarily using 0.8, which requires a little bit of hand
+ # movement on the touchpads used for testing.
+ # Note that the directions are based on the default natural
+ # scrolling settings in GNOME settings.
+ if delta_x > 0.8:
+ event_direction = Direction("left")
+ elif delta_x < -0.8:
+ event_direction = Direction("right")
+ if delta_y > 0.8:
+ event_direction = Direction("up")
+ elif delta_y < -0.8:
+ event_direction = Direction("down")
+ if event_direction:
+ if direction.value == event_direction.value:
+ self.found_direction(direction)
+ break
return True
diff --git a/bin/virtualization.py b/bin/virtualization.py
index a3794de..034554b 100755
--- a/bin/virtualization.py
+++ b/bin/virtualization.py
@@ -557,149 +557,6 @@ class RunCommand(object):
self.returncode = proc.returncode
-class UVTKVMTest(object):
-
- def __init__(self, image=None):
- self.image = image
- self.release = get_codename_to_test()
- self.arch = check_output(['dpkg', '--print-architecture'],
- universal_newlines=True).strip()
- # max len(name) is 8 chars for use with test-snapd-uvtool snap
- self.name = tempfile.mktemp()[-8:]
-
- def run_command(self, cmd):
- task = RunCommand(cmd)
- if task.returncode != 0:
- logging.error('Command {} returnd a code of {}'.format(
- task.cmd, task.returncode))
- logging.error(' STDOUT: {}'.format(task.stdout))
- logging.error(' STDERR: {}'.format(task.stderr))
- return False
- else:
- logging.debug('Command {}:'.format(task.cmd))
- if task.stdout != '':
- logging.debug(' STDOUT: {}'.format(task.stdout))
- elif task.stderr != '':
- logging.debug(' STDERR: {}'.format(task.stderr))
- else:
- logging.debug(' Command returned no output')
- return True
-
- def ssh_command(self, private_key_file, cmd):
- task = RunCommand("uvt-kvm ip {}".format(self.name))
- if task.returncode != 0:
- logging.error('Command {} returnd a code of {}'.format(
- task.cmd, task.returncode))
- return False
- vm_ip = task.stdout
-
- ssh_cmd = ('ssh ubuntu@{} '
- '-o UserKnownHostsFile=/dev/null '
- '-o StrictHostKeyChecking=no '
- '-i {} {}').format(vm_ip, private_key_file, cmd)
- return self.run_command(ssh_cmd)
-
- def get_image_or_source(self):
- """
- An image can be specifed in a filesytem path and used directly in
- uvt-create with the backing-image option or a url can be
- specifed and used in uvt-simplestreams to generate an image.
- """
- url = urlparse(self.image)
-
- if url.scheme == 'file' or os.path.isfile(url.path):
- logging.debug("Cloud image exists locally at %s" % url.path)
- self.image = url.path
- else:
- cmd = ("uvt-simplestreams-libvirt sync release={} "
- "arch={}".format(self.release, self.arch))
-
- if url.scheme == 'http':
- # Path specified to use -source option
- logging.debug("Using --source option for uvt-simpletreams")
- cmd = cmd + " --source {} ".format(self.image)
-
- logging.debug("uvt-simplestreams-libvirt sync")
- if not self.run_command(cmd):
- return False
- return True
-
- def cleanup(self):
- """
- A combination of virsh destroy/undefine is used instead of
- uvt-kvm destroy. When using uvt-kvm destroy the following bug
- is seen:
- https://bugs.launchpad.net/ubuntu/+source/uvtool/+bug/1452095
- """
- # Destroy vm
- logging.debug("Destroy VM")
- if not self.run_command('virsh destroy {}'.format(self.name)):
- return False
-
- # Virsh undefine
- logging.debug("Undefine VM")
- if not self.run_command('virsh undefine {}'.format(self.name)):
- return False
-
- # Purge/Remove simplestreams image
- if not self.run_command("uvt-simplestreams-libvirt purge"):
- return False
- return True
-
- def start(self):
- # Generate ssh key if needed
- with tempfile.TemporaryDirectory(
- dir=os.path.expandvars("$PLAINBOX_SESSION_SHARE")) as tmp_dir:
- ssh_private_key_file = "{}/id_rsa".format(tmp_dir)
- ssh_public_key_file = "{}/id_rsa.pub".format(tmp_dir)
-
- if not os.path.exists(ssh_private_key_file):
- cmd = ('ssh-keygen -f {} -t rsa -N \'\''.format(
- ssh_private_key_file))
- if not self.run_command(cmd):
- return False
-
- # Create vm
- logging.debug("Creating VM")
- cmd = ('uvt-kvm create --ssh-public-key-file {} {} arch={}'.format(
- ssh_public_key_file, self.name, self.arch))
-
- logging.debug("Checking for local image")
- try:
- self.image.find(".img") > 0
- except AttributeError:
- logging.debug("No user provided image found.")
- logging.debug(
- "I will attempt to sync the image from ubuntu.com")
- else:
- cmd = cmd + " --backing-image-file {} ".format(self.image)
-
- if not self.run_command(cmd):
- return False
-
- logging.debug("Wait for VM to complete creation")
- cmd = 'uvt-kvm wait --ssh-private-key-file {} {}'.format(
- ssh_private_key_file, self.name)
- if not self.run_command(cmd):
- return False
-
- logging.debug("List newly created vm")
- cmd = ("uvt-kvm list")
- if not self.run_command(cmd):
- return False
-
- logging.debug("Verify VM was created with ssh")
- if not self.ssh_command(ssh_private_key_file, ""):
- return False
-
- logging.debug("Verify VM was created with ssh and run a command")
- cmd = "lsb_release -a"
- if not self.ssh_command(ssh_private_key_file, cmd):
- return False
-
- return True
-
-
class LXDTest(object):
def __init__(self, template=None, rootfs=None):
@@ -865,25 +722,208 @@ class LXDTest(object):
return True
-def test_uvtkvm(args):
- logging.debug("Executing UVT KVM Test")
- # if args.image is not set and UVT_IMAGE_OR_SOURCE does not exist, a key
- # error is generated we need to handle.
- try:
- image = args.image or os.environ['UVT_IMAGE_OR_SOURCE']
- except KeyError:
- logging.warning("UVT_IMAGE_OR_SOURCE is not set")
- image = None
- uvt_test = UVTKVMTest(image)
- uvt_test.get_image_or_source()
- result = uvt_test.start()
- uvt_test.cleanup()
+class LXDTest_vm(object):
+
+ def __init__(self, template=None, image=None):
+ self.image_url = image
+ self.template_url = template
+ self.image_tarball = None
+ self.template_tarball = None
+ self.name = 'testbed'
+ self.image_alias = uuid4().hex
+ self.default_remote = "ubuntu:"
+ self.os_version = get_release_to_test()
+
+ def run_command(self, cmd):
+ task = RunCommand(cmd)
+ if task.returncode != 0:
+ logging.error('Command {} returnd a code of {}'.format(
+ task.cmd, task.returncode))
+ logging.error(' STDOUT: {}'.format(task.stdout))
+ logging.error(' STDERR: {}'.format(task.stderr))
+ return False
+ else:
+ logging.debug('Command {}:'.format(task.cmd))
+ if task.stdout != '':
+ logging.debug(' STDOUT: {}'.format(task.stdout))
+ elif task.stderr != '':
+ logging.debug(' STDERR: {}'.format(task.stderr))
+ else:
+ logging.debug(' Command returned no output')
+ return True
+
+ def setup(self):
+ # Initialize LXD
+ result = True
+ logging.debug("Attempting to initialize LXD")
+ # TODO: Need a method to see if LXD is already initialized
+ if not self.run_command('lxd init --auto'):
+ logging.debug('Error encounterd while initializing LXD')
+ result = False
+
+ # Retrieve and insert LXD images
+ if self.template_url is not None:
+ logging.debug("Downloading template.")
+ targetfile = urlparse(self.template_url).path.split('/')[-1]
+ filename = os.path.join('/tmp', targetfile)
+ if not os.path.isfile(filename):
+ self.template_tarball = self.download_images(self.template_url,
+ filename)
+ if not self.template_tarball:
+ logging.error("Unable to download {} from "
+ "{}".format(self.template_tarball,
+ self.template_url))
+ logging.error("Aborting")
+ result = False
+ else:
+ logging.debug("Template file {} already exists. "
+ "Skipping Download.".format(filename))
+ self.template_tarball = filename
+
+ if self.image_url is not None:
+ logging.debug("Downloading image.")
+ targetfile = urlparse(self.image_url).path.split('/')[-1]
+ filename = os.path.join('/tmp', targetfile)
+ if not os.path.isfile(filename):
+ self.image_tarball = self.download_images(self.image_url,
+ filename)
+ if not self.image_tarball:
+ logging.error("Unable to download {} from{}".format(
+ self.image_tarball, self.image_url))
+ logging.error("Aborting")
+ result = False
+ else:
+ logging.debug("Template file {} already exists. "
+ "Skipping Download.".format(filename))
+ self.image_tarball = filename
+
+ # Insert images
+ if self.template_url is not None and self.image_url is not None:
+ logging.debug("Importing images into LXD")
+ cmd = 'lxc image import {} {} --alias {}'.format(
+ self.template_tarball, self.image_tarball,
+ self.image_alias)
+ result = self.run_command(cmd)
+ if not result:
+ logging.error('Error encountered while attempting to '
+ 'import images into LXD')
+ result = False
+ return result
+
+ def download_images(self, url, filename):
+ """
+ Downloads LXD files for same release as host machine
+ """
+ # TODO: Clean this up to use a non-internet simplestream on MAAS server
+ logging.debug("Attempting download of {} from {}".format(filename,
+ url))
+ try:
+ urllib.request.urlretrieve(url, filename)
+ except (IOError,
+ OSError,
+ urllib.error.HTTPError,
+ urllib.error.URLError) as exception:
+ logging.error("Failed download of image from %s: %s",
+ url, exception)
+ return False
+ except ValueError as verr:
+ logging.error("Invalid URL %s" % url)
+ logging.error("%s" % verr)
+ return False
+
+ if not os.path.isfile(filename):
+ logging.warn("Can not find {}".format(filename))
+ return False
+
+ return filename
+ def cleanup(self):
+ """
+ Clean up test files an Virtual Machines created
+ """
+ logging.debug('Cleaning up images and VMs created during test')
+ self.run_command('lxc image delete {}'.format(self.image_alias))
+ self.run_command('lxc delete --force {}'.format(self.name))
+
+ def start_vm(self):
+ """
+ Creates an lxd virtutal machine and performs the test
+ """
+ wait_interval = 5
+ test_interval = 300
+
+ result = self.setup()
+ if not result:
+ logging.error("One or more setup stages failed.")
+ return False
+
+ # Create Virtual Machine
+ logging.debug("Launching Virtual Machine")
+ if not self.image_url and not self.template_url:
+ logging.debug("No local image available, attempting to "
+ "import from default remote.")
+ cmd = ('lxc init {}{} {} --vm '.format(
+ self.default_remote, self.os_version, self.name))
+ else:
+ cmd = ('lxc init {} {} --vm'.format(self.image_alias, self.name))
+
+ if not self.run_command(cmd):
+ return False
+
+ logging.debug("Start VM:")
+ cmd = ("lxc start {} ".format(self.name))
+ if not self.run_command(cmd):
+ return False
+
+ logging.debug("Virtual Machine listing:")
+ cmd = ("lxc list")
+ if not self.run_command(cmd):
+ return False
+
+ logging.debug("Wait for vm to boot")
+ check_vm = 0
+ while check_vm < test_interval:
+ time.sleep(wait_interval)
+ cmd = ("lxc exec {} -- lsb_release -a".format(self.name))
+ if self.run_command(cmd):
+ print("Vm started and booted succefully")
+ return True
+ else:
+ logging.debug("Re-verify VM booted")
+ check_vm = check_vm + wait_interval
+
+ logging.debug("testing vm failed")
+ if check_vm == test_interval:
+ return False
+
+
+def test_lxd_vm(args):
+ logging.debug("Executing LXD VM Test")
+
+ template = None
+ image = None
+
+ # First in priority are environment variables.
+ if 'LXD_TEMPLATE' in os.environ:
+ template = os.environ['LXD_TEMPLATE']
+ if 'KVM_IMAGE' in os.environ:
+ image = os.environ['KVM_IMAGE']
+
+ # Finally, highest-priority are command line arguments.
+ if args.template:
+ template = args.template
+ if args.image:
+ image = args.image
+
+ lxd_test = LXDTest_vm(template, image)
+
+ result = lxd_test.start_vm()
+ lxd_test.cleanup()
if result:
- print("PASS: VM was succssfully started and checked")
+ print("PASS: Virtual Machine was succssfully started and checked")
sys.exit(0)
else:
- print("FAIL: VM was not started and/or checked")
+ print("FAIL: Virtual Machine was not started and checked")
sys.exit(1)
@@ -961,10 +1001,10 @@ def main():
# Main cli options
kvm_test_parser = subparsers.add_parser(
'kvm', help=("Run kvm virtualization test"))
- uvt_kvm_test_parser = subparsers.add_parser(
- 'uvt', help=("Run uvt kvm virtualization test"))
lxd_test_parser = subparsers.add_parser(
'lxd', help=("Run the LXD validation test"))
+ lxd_test_vm_parser = subparsers.add_parser(
+ 'lxdvm', help=("Run the LXD VM validation test"))
parser.add_argument('--debug', dest='log_level',
action="store_const", const=logging.DEBUG,
default=logging.INFO)
@@ -980,16 +1020,19 @@ def main():
kvm_test_parser.set_defaults(func=test_kvm)
# Sub test options
- uvt_kvm_test_parser.add_argument(
- '-i', '--image', type=str)
- uvt_kvm_test_parser.set_defaults(func=test_uvtkvm)
-
lxd_test_parser.add_argument(
'--template', type=str, default=None)
lxd_test_parser.add_argument(
'--rootfs', type=str, default=None)
lxd_test_parser.set_defaults(func=test_lxd)
+ # Sub test options
+ lxd_test_vm_parser.add_argument(
+ '--template', type=str, default=None)
+ lxd_test_vm_parser.add_argument(
+ '--image', type=str, default=None)
+ lxd_test_vm_parser.set_defaults(func=test_lxd_vm)
+
args = parser.parse_args()
try:
diff --git a/bin/wifi_nmcli_test.py b/bin/wifi_nmcli_test.py
index 936af9a..818d4cf 100755
--- a/bin/wifi_nmcli_test.py
+++ b/bin/wifi_nmcli_test.py
@@ -48,7 +48,7 @@ def cleanup_nm_connections():
print_cmd(cmd)
output = sp.check_output(cmd, shell=True)
for line in output.decode(sys.stdout.encoding).splitlines():
- type, uuid, name = line.strip().split(':')
+ type, uuid, name = line.strip().split(':', 2)
if type == '802-11-wireless':
print("Deleting connection", name)
cmd = "nmcli c delete {}".format(uuid)
@@ -83,7 +83,7 @@ def list_aps(args):
# lp bug #1723372 - extra line in output on zesty
if line.strip() == args.device:
continue
- ssid, channel, frequency, signal = line.strip().split(':')
+ ssid, channel, frequency, signal = line.strip().rsplit(':', 3)
print("SSID: {} Chan: {} Freq: {} Signal: {}".format(
ssid, channel, frequency, signal))
if hasattr(args, 'essid'):
diff --git a/manage.py b/manage.py
index 116779b..331abc8 100755
--- a/manage.py
+++ b/manage.py
@@ -5,7 +5,7 @@ from plainbox.provider_manager import N_
setup(
name='plainbox-provider-checkbox',
namespace='com.canonical.certification',
- version="0.64.0",
+ version="0.65.0rc1",
description=N_("Checkbox provider"),
gettext_domain='plainbox-provider-checkbox',
strict=False, deprecated=False,
diff --git a/units/graphics/jobs.pxu b/units/graphics/jobs.pxu
index 4dde304..8206f1a 100644
--- a/units/graphics/jobs.pxu
+++ b/units/graphics/jobs.pxu
@@ -133,13 +133,7 @@ category_id: com.canonical.plainbox::graphics
requires:
device.category == 'VIDEO'
command:
- # shellcheck disable=SC1091
- source graphics_env.sh {driver} {index}
- maxi="$(xrandr -q |grep -A 1 "connected\( primary\)* [0-9]" |tail -1 |awk '{{print $1}}')"
- now="$(python3 -c 'from gi.repository import Gdk; screen=Gdk.Screen.get_default(); geo = screen.get_monitor_geometry(screen.get_primary_monitor()); print(geo.width, "x", geo.height, sep="")')"
- test "$maxi" != "$now" && notify="\nPlease switch to the maximum resolution \nfor every graphic tests"
- echo "Maximum resolution: $maxi"
- echo "Current resolution: $now $notify"
+ graphics_max_resolution.py
estimated_duration: 10.0
_summary: Test maximum supported resolution for {vendor} {product}
_description:
@@ -154,16 +148,6 @@ _description:
unit: template
template-resource: graphics_card
-id: graphics/{index}_modes_{product_slug}
-plugin: shell
-category_id: com.canonical.plainbox::graphics
-command: graphics_modes_info.py
-estimated_duration: 0.250
-_description: Collect info on graphics modes (screen resolution and refresh rate) for {vendor} {product}
-_summary: Test graphic modes info for {vendor} {product}
-
-unit: template
-template-resource: graphics_card
id: graphics/{index}_color_depth_{product_slug}
plugin: shell
category_id: com.canonical.plainbox::graphics
diff --git a/units/graphics/legacy.pxu b/units/graphics/legacy.pxu
index 14969b0..2b9545d 100644
--- a/units/graphics/legacy.pxu
+++ b/units/graphics/legacy.pxu
@@ -96,14 +96,6 @@ _description:
VERIFICATION:
Is this the display's maximum resolution?
-id: graphics/modes
-plugin: shell
-category_id: com.canonical.plainbox::graphics
-command: graphics_modes_info.py
-estimated_duration: 0.250
-_description: Collect info on graphics modes (screen resolution and refresh rate)
-_summary: Collect info on graphics modes
-
id: graphics/color_depth
plugin: shell
category_id: com.canonical.plainbox::graphics
diff --git a/units/graphics/test-plan.pxu b/units/graphics/test-plan.pxu
index 8725b0d..fe7d4a1 100644
--- a/units/graphics/test-plan.pxu
+++ b/units/graphics/test-plan.pxu
@@ -32,9 +32,6 @@ _description:
Graphics tests (integrated GPU) (Automated)
include:
graphics/1_auto_switch_card_.* certification-status=blocker
- graphics/xorg-version certification-status=blocker
- graphics/xorg-failsafe certification-status=blocker
- graphics/xorg-process certification-status=blocker
graphics/VESA_drivers_not_in_use certification-status=blocker
graphics/1_driver_version_.* certification-status=blocker
graphics/1_gl_support_.* certification-status=blocker
@@ -169,9 +166,6 @@ _name: Graphics tests (integrated GPU, certification blockers only)
_description: Graphics tests (integrated GPU, certification blockers only)
include:
graphics/1_auto_switch_card_.* certification-status=blocker
- graphics/xorg-version certification-status=blocker
- graphics/xorg-failsafe certification-status=blocker
- graphics/xorg-process certification-status=blocker
graphics/VESA_drivers_not_in_use certification-status=blocker
graphics/1_maximum_resolution_.* certification-status=blocker
graphics/1_glxgears_.* certification-status=blocker
diff --git a/units/miscellanea/jobs.pxu b/units/miscellanea/jobs.pxu
index 785eebe..7e13164 100644
--- a/units/miscellanea/jobs.pxu
+++ b/units/miscellanea/jobs.pxu
@@ -396,12 +396,20 @@ plugin: shell
category_id: com.canonical.plainbox::miscellanea
estimated_duration: 0.1
id: miscellanea/get_maas_version
-command: maas-version-check.sh
+command: install-method-check.sh --maas
_description: If system was installed via MAAS from a cert server, the MAAS version used should be contained in /etc/installed-by-maas
_summary: Verify MAAS version used to deploy the SUT
plugin: shell
category_id: com.canonical.plainbox::miscellanea
+estimated_duration: 0.1
+id: miscellanea/test_iso_install
+command: install-method-check.sh --iso
+_description: Test that the system was installed via a Subiquity ISO image
+_summary: Test that the system was installed via a Subiquity ISO image
+
+plugin: shell
+category_id: com.canonical.plainbox::miscellanea
estimated_duration: 30.0
id: miscellanea/get_make_and_model
user: root
diff --git a/units/monitor/jobs.pxu b/units/monitor/jobs.pxu
index 46ecb20..ec57b59 100644
--- a/units/monitor/jobs.pxu
+++ b/units/monitor/jobs.pxu
@@ -136,14 +136,16 @@ flags: also-after-suspend
id: monitor/{index}_powersaving_{product_slug}
plugin: user-interact-verify
category_id: com.canonical.plainbox::monitor
-command: xset dpms force off
+command:
+ busctl --user call org.gnome.Shell /org/gnome/ScreenSaver org.gnome.ScreenSaver SetActive b true
+ sleep 5
+ busctl --user call org.gnome.Shell /org/gnome/ScreenSaver org.gnome.ScreenSaver SetActive b false
_purpose:
This test will check your monitor power saving capabilities
_steps:
- 1. Click "Test" to try the power saving capabilities of your monitor
- 2. Press any key or move the mouse to recover
+ 1. Start the test to try the power saving capabilities of your monitor
_verification:
- Did the monitor go blank and turn on again?
+ Did the monitor go blank and turn on again after a few seconds?
unit: template
template-resource: graphics_card
diff --git a/units/touchpad/jobs.pxu b/units/touchpad/jobs.pxu
index 95dfc5b..11960ee 100644
--- a/units/touchpad/jobs.pxu
+++ b/units/touchpad/jobs.pxu
@@ -102,37 +102,20 @@ _siblings:
plugin: user-interact
category_id: com.canonical.plainbox::touchpad
-id: touchpad/multitouch-horizontal
+id: touchpad/multitouch
requires: dmi.product in ['Notebook','Laptop','Portable','Convertible']
-command: touchpad_test.py right left
+command: touchpad_test.py up down right left
estimated_duration: 120.0
_purpose:
- Touchpad 2-touch horizontal scroll verification
+ Touchpad 2-finger scroll verification
_steps:
- 1. Select "Test" when ready and place your cursor within the borders of the displayed test window.
- 2. Verify that you can move the horizontal slider by moving 2 fingers right and left along the touchpad.
-_verification:
- Could you scroll right and left?
-_siblings:
- [{ "id": "touchpad/multitouch-horizontal-after-suspend",
- "depends": "suspend/suspend_advanced_auto touchpad/multitouch-horizontal" }]
-
-plugin: user-interact
-category_id: com.canonical.plainbox::touchpad
-id: touchpad/multitouch-vertical
-requires: dmi.product in ['Notebook','Laptop','Portable','Convertible']
-command: touchpad_test.py up down
-estimated_duration: 120.0
-_purpose:
- Touchpad 2-touch vertical scroll verification
-_steps:
- 1. Select "Test" when ready and place your cursor within the borders of the displayed test window.
- 2. Verify that you can move the vertical slider by moving 2 fingers up and down along the touchpad.
+ 1. Press "Enter" when ready and place your cursor within the borders of the displayed test window.
+ 2. Verify that the scroll events are detected by moving two fingers in all four directions along the touchpad.
_verification:
- Could you scroll up and down?
+ Were scroll events detected for all four directions?
_siblings:
- [{ "id": "touchpad/multitouch-vertical-after-suspend",
- "depends": "suspend/suspend_advanced_auto touchpad/multitouch-vertical" }]
+ [{ "id": "touchpad/multitouch-after-suspend",
+ "depends": "suspend/suspend_advanced_auto touchpad/multitouch" }]
plugin: manual
category_id: com.canonical.plainbox::touchpad
@@ -238,6 +221,7 @@ _description:
id: touchpad/palm-rejection
plugin: user-interact
category_id: com.canonical.plainbox::touchpad
+requires: dmi.product in ['Notebook','Laptop','Portable','Convertible']
command: qmlscene -qt5 --fullscreen "$PLAINBOX_PROVIDER_DATA"/palm_rejection.qml 2>&1 | grep -o PASS
_purpose:
This test checks if touchpad ignores palm touches
diff --git a/units/touchpad/test-plan.pxu b/units/touchpad/test-plan.pxu
index e03ea54..74d41a5 100644
--- a/units/touchpad/test-plan.pxu
+++ b/units/touchpad/test-plan.pxu
@@ -20,8 +20,7 @@ include:
touchpad/singletouch-selection certification-status=blocker
touchpad/drag-and-drop certification-status=blocker
touchpad/multitouch-rightclick certification-status=blocker
- touchpad/multitouch-horizontal certification-status=blocker
- touchpad/multitouch-vertical certification-status=blocker
+ touchpad/multitouch certification-status=blocker
id: touchpad-cert-automated
unit: test plan
@@ -45,8 +44,7 @@ include:
touchpad/singletouch-selection-after-suspend certification-status=blocker
touchpad/drag-and-drop-after-suspend certification-status=blocker
touchpad/multitouch-rightclick-after-suspend certification-status=blocker
- touchpad/multitouch-horizontal-after-suspend certification-status=blocker
- touchpad/multitouch-vertical-after-suspend certification-status=blocker
+ touchpad/multitouch-after-suspend certification-status=blocker
id: touchpad-cert-blockers
unit: test plan
@@ -59,8 +57,7 @@ include:
touchpad/singletouch-selection certification-status=blocker
touchpad/drag-and-drop certification-status=blocker
touchpad/multitouch-rightclick certification-status=blocker
- touchpad/multitouch-horizontal certification-status=blocker
- touchpad/multitouch-vertical certification-status=blocker
+ touchpad/multitouch certification-status=blocker
id: after-suspend-touchpad-cert-blockers
unit: test plan
@@ -73,5 +70,4 @@ include:
touchpad/singletouch-selection-after-suspend certification-status=blocker
touchpad/drag-and-drop-after-suspend certification-status=blocker
touchpad/multitouch-rightclick-after-suspend certification-status=blocker
- touchpad/multitouch-horizontal-after-suspend certification-status=blocker
- touchpad/multitouch-vertical-after-suspend certification-status=blocker
+ touchpad/multitouch-after-suspend certification-status=blocker
diff --git a/units/virtualization/jobs.pxu b/units/virtualization/jobs.pxu
index 2b903c7..f203910 100644
--- a/units/virtualization/jobs.pxu
+++ b/units/virtualization/jobs.pxu
@@ -1,21 +1,16 @@
plugin: shell
category_id: com.canonical.plainbox::virtualization
-id: virtualization/kvm_check_vm
-user: root
-environ: UVT_IMAGE_OR_SOURCE http_proxy https_proxy
-estimated_duration: 300.0
+id: virtualization/verify_lxd_vm
+environ: LXD_TEMPLATE KVM_IMAGE
+estimated_duration: 60.0
requires:
- executable.name == 'uvt-kvm'
- executable.name == 'uvt-simplestreams-libvirt'
- executable.name == 'virsh'
- executable.name == 'ssh-keygen'
- virtualization.kvm == 'supported'
-command: virtualization.py --debug uvt
+ executable.name == 'lxc'
+ package.name == 'lxd' or snap.name == 'lxd'
+command: virtualization.py --debug lxdvm
_description:
- Verifies that a KVM guest can be created and booted using an Ubuntu Server
- cloud image.
+ Verifies that an LXD Virtual Machine can be created and launched
_summary:
- Verify KVM guest boots
+ Verify LXD Virtual Machine launches
plugin: shell
category_id: com.canonical.plainbox::virtualization
diff --git a/units/virtualization/test-plan.pxu b/units/virtualization/test-plan.pxu
index 16f4c73..aae210f 100644
--- a/units/virtualization/test-plan.pxu
+++ b/units/virtualization/test-plan.pxu
@@ -15,5 +15,5 @@ id: virtualization-automated
unit: test plan
_name: Automated virtualization tests
include:
- virtualization/kvm_check_vm
- virtualization/verify_lxd \ No newline at end of file
+ virtualization/verify_lxd
+ virtualization/verify_lxd_vm
diff --git a/units/zapper/jobs.pxu b/units/zapper/jobs.pxu
index acf65f1..91d1d38 100644
--- a/units/zapper/jobs.pxu
+++ b/units/zapper/jobs.pxu
@@ -5,3 +5,41 @@ estimated_duration: 60
_summary: Check if the system automatically changes the resolution based on EDID
environ: ZAPPER_HOST
command: edid_cycle.py "$ZAPPER_HOST"
+
+unit: template
+template-resource: zapper_capabilities
+template-filter: zapper_capabilities.capability == 'USB hotplug' and zapper_capabilities.usb_version == '3'
+category_id: com.canonical.plainbox::usb
+id: usb3/zapper-usb-insert-{{ port_alias }}
+template-engine: jinja2
+_summary: Zapper-driven USB insertion test on port {{ port_alias }}
+requires: usb.usb3 == 'supported'
+plugin: shell
+user: root
+environ: ZAPPER_ADDRESS
+command:
+ {%- if __on_ubuntucore__ %}
+ checkbox-support-run_watcher --zapper-usb-address={{ port_alias }} insertion usb3
+ {%- else %}
+ removable_storage_watcher.py --unmounted -m 500000000 --zapper-usb-address={{ port_alias }} insert usb
+ {% endif -%}
+estimated_duration: 20
+
+unit: template
+template-resource: zapper_capabilities
+template-filter: zapper_capabilities.capability == 'USB hotplug' and zapper_capabilities.usb_version == '3'
+category_id: com.canonical.plainbox::usb
+id: usb3/zapper-usb-remove-{{ port_alias }}
+template-engine: jinja2
+_summary: Zapper-driven USB removal test on port {{ port_alias }}
+requires: usb.usb3 == 'supported'
+plugin: shell
+user: root
+environ: ZAPPER_ADDRESS
+command:
+ {%- if __on_ubuntucore__ %}
+ checkbox-support-run_watcher --zapper-usb-address={{ port_alias }} removal usb3
+ {%- else %}
+ removable_storage_watcher.py --unmounted -m 500000000 --zapper-usb-address={{ port_alias }} remove usb
+ {% endif -%}
+estimated_duration: 20
diff --git a/units/zapper/resource.pxu b/units/zapper/resource.pxu
index 81b9d19..fcfe8a8 100644
--- a/units/zapper/resource.pxu
+++ b/units/zapper/resource.pxu
@@ -1,5 +1,8 @@
-id: pig
+id: zapper_capabilities
plugin: resource
-_summary: Get Pig's capabilities
-environ: PIG_HOST
-command: ssh -q -o 'StrictHostKeyChecking=no' "$PIG_HOST" /snap/bin/pigbox capabilities
+_summary: Get Zapper's setup capabilities
+_description:
+ Connect to Zapper and list functions that the current setup (DUT + Zapper) are
+ capable off.
+environ: ZAPPER_HOST
+command: checkbox-support-zapper-proxy get_capabilities
diff --git a/units/zapper/test-plan.pxu b/units/zapper/test-plan.pxu
index ce287ff..889fb50 100644
--- a/units/zapper/test-plan.pxu
+++ b/units/zapper/test-plan.pxu
@@ -11,6 +11,8 @@ unit: test plan
_name: Tests using Zapper
_description: Tests using Zapper
include:
- monitor/pig-edid
+ monitor/zapper-edid
+ usb3/zapper-usb-insert-.*
+ usb3/zapper-usb-remove-.*
bootstrap_include:
- pig
+ zapper_capabilities