diff options
-rwxr-xr-x | bin/gnome_randr_cycle.py | 139 | ||||
-rwxr-xr-x | bin/rotation_test.py | 14 | ||||
-rwxr-xr-x | bin/xrandr_cycle.py | 109 | ||||
-rw-r--r-- | units/graphics/jobs.pxu | 7 | ||||
-rw-r--r-- | units/graphics/packaging.pxu | 8 | ||||
-rw-r--r-- | units/suspend/suspend-graphics.pxu | 7 | ||||
-rw-r--r-- | units/suspend/suspend.pxu | 24 |
7 files changed, 201 insertions, 107 deletions
diff --git a/bin/gnome_randr_cycle.py b/bin/gnome_randr_cycle.py new file mode 100755 index 0000000..cf0a7cf --- /dev/null +++ b/bin/gnome_randr_cycle.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +# +# This file is part of Checkbox. +# +# Copyright 2022 Canonical Ltd. +# Written by: +# Sylvain Pineau <sylvain.pineau@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 subprocess +import sys +import tarfile +import time + +from collections import OrderedDict +from fractions import Fraction + +parser = argparse.ArgumentParser() +parser.add_argument('--keyword', default='', + help=('A keyword to distinguish the screenshots ' + 'taken in this run of the script')) +parser.add_argument('--screenshot-dir', + default=os.environ['HOME'], + help=('Specify a directory to store screenshots in. ' + 'Default is %(default)s')) +args = parser.parse_args() + +randrinfo = subprocess.Popen( + 'gnome-randr', shell=True, stdout=subprocess.PIPE) +output = randrinfo.communicate()[0].decode().split('\n') + +monitor = '' +monitors = dict() +highest_modes = [] # list of highest-res modes for each aspect ratio +current_modes = [] # remember the user's current settings for cleanup later +failures = 0 # count the number of failed modesets + +for line in output: + # Ignore Interlaced modes that are indicated by presence of a + # trailing 'i' character. + if ':' in line or line == '' or 'i@' in line: + continue + if not (line.startswith(' ') or line.startswith('\t')): + try: + monitor = line.split()[0] + monitors[monitor] = OrderedDict() + continue + except IndexError: + continue + if monitor: + modeline = line.split() + try: + mode, resolution, rate = modeline[:3] + width, height = [int(x) for x in resolution.split('x')] + aspect = Fraction(width, height) + if width < 675 or width / aspect < 530: + continue + if resolution in monitors[monitor]: + existing_rate = monitors[monitor][resolution][4] + if rate < existing_rate: + continue + monitors[monitor][resolution] = (width, aspect, mode, rate) + except IndexError: + continue + +for monitor in monitors.keys(): + # let's create a dict of aspect_ratio:largest_width for each display + # (width, because it's easier to compare simple ints when looking for the + # highest value). + top_res_per_aspect = OrderedDict() + connected = False + for resolution in monitors[monitor]: + width, aspect, mode, rate = monitors[monitor][resolution] + cur_max = top_res_per_aspect.get(aspect, 0) + top_res_per_aspect[aspect] = max(cur_max, width) + if '*' in rate: + connected = True + current_modes.append((monitor, resolution, mode, rate)) + if not connected: + continue + for aspect_ratio, max_width in reversed(top_res_per_aspect.items()): + for resolution in monitors[monitor]: + width, aspect, mode, rate = monitors[monitor][resolution] + if aspect == aspect_ratio and width == max_width: + highest_modes.append((monitor, resolution, mode, rate)) + +screenshot_path = os.path.join(args.screenshot_dir, 'xrandr_screens') + +if args.keyword: + screenshot_path = screenshot_path + '_' + args.keyword +os.makedirs(screenshot_path, exist_ok=True) + +for monitor, resolution, mode, rate in highest_modes + current_modes: + rate = rate.replace('+', '').replace('*', '') + print("Set mode {}@{} for output {}".format(resolution, rate, monitor), + flush=True) + cmd = 'gnome-randr modify ' + monitor + ' -m ' + mode + try: + subprocess.run(cmd, check=True, shell=True, stdout=subprocess.PIPE) + mode_string = monitor + '_' + resolution + filename = os.path.join(screenshot_path, mode_string + '.jpg') + cmd = 'gnome-screenshot -f ' + filename + result = subprocess.run(cmd, shell=True, check=False) + if result.returncode != 0: + print("Could not capture screenshot -\n" + "you may need to install the package 'gnome-screenshot'.", + file=sys.stderr, flush=True) + except subprocess.CalledProcessError: + failures = failures + 1 + print('Failed to set mode {} for output {}:'.format(mode, monitor), + file=sys.stderr, flush=True) + print(' {}'.format(cmd), file=sys.stderr, flush=True) + time.sleep(8) # let the hardware recover a bit + +# Tar up the screenshots for uploading +try: + with tarfile.open(screenshot_path + '.tgz', 'w:gz') as screen_tar: + for screen in os.listdir(screenshot_path): + screen_tar.add(screenshot_path + '/' + screen, screen) +except (IOError, OSError): + pass + +if failures != 0: + exit(1) +else: + exit(0) diff --git a/bin/rotation_test.py b/bin/rotation_test.py index f3467b8..d6a5b47 100755 --- a/bin/rotation_test.py +++ b/bin/rotation_test.py @@ -23,8 +23,11 @@ # along with Checkbox. If not, see <http://www.gnu.org/licenses/>. import gi +import os import time import subprocess +import warnings +warnings.filterwarnings("ignore", category=DeprecationWarning) gi.require_version('Gdk', '3.0') from gi.repository import Gdk # noqa: E402 @@ -35,11 +38,14 @@ def main(): screen = Gdk.Screen.get_default() output = screen.get_monitor_plug_name(screen.get_primary_monitor()) print("Using output: {}".format(output)) - for rotation in ['right', 'inverted', 'left', 'normal']: - print("Changing rotation to: {}".format(rotation)) - subprocess.check_call( - ['xrandr', '--output', output, '--rotation', rotation]) + if os.getenv('XDG_SESSION_TYPE') == 'wayland': + subprocess.check_call( + ['gnome-randr', 'modify', output, '--rotate', rotation]) + else: + print("setting rotation to {}".format(rotation)) + subprocess.check_call( + ['xrandr', '--output', output, '--rotation', rotation]) time.sleep(8) diff --git a/bin/xrandr_cycle.py b/bin/xrandr_cycle.py index 6c9afc5..819f12a 100755 --- a/bin/xrandr_cycle.py +++ b/bin/xrandr_cycle.py @@ -1,10 +1,7 @@ #!/usr/bin/env python3 import argparse -import errno import os -import re -import shutil import subprocess import sys import tarfile @@ -110,106 +107,30 @@ for adapter, params in top_res_per_aspect.items(): highest_modes.append((adapter, mode)) # Now we have a list of the modes we need to test. So let's do just that. -profile_path = os.environ['HOME'] + '/.shutter/profiles/' screenshot_path = os.path.join(args.screenshot_dir, 'xrandr_screens') -# Where to find the shutter.xml template? Two possible locations. -shutter_xml_template = None - -if 'PLAINBOX_PROVIDER_DATA' in os.environ: - shutter_xml_template = os.path.join(os.environ['PLAINBOX_PROVIDER_DATA'], - "settings", "shutter.xml") -else: - shutter_xml_template = os.path.join( - os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], - "data", - "settings", - "shutter.xml") - if args.keyword: screenshot_path = screenshot_path + '_' + args.keyword - -regex = re.compile(r'filename="[^"\r\n]*"') - -# Keep the shutter profile in place before starting - -# Any errors creating the directories or copying the template is fatal, -# since things won't work if we fail. -try: - os.makedirs(profile_path, exist_ok=True) - os.makedirs(screenshot_path, exist_ok=True) -except OSError as excp: - raise SystemExit("ERROR: Unable to create " - "required directories: {}".format(excp)) - -try: - if os.path.exists(profile_path) and os.path.isfile(profile_path): - try: - os.remove(profile_path) - except PermissionError as exc: - print("Warning: could not remove {}. {}".format( - profile_path, exc)) - else: - shutil.copy(shutter_xml_template, profile_path) -except (IOError, OSError) as excp: - print("ERROR: Unable to copy {} to {}: {}".format(shutter_xml_template, - profile_path, - excp)) - if excp.errno == errno.ENOENT: - print("Try setting PLAINBOX_PROVIDER_DATA to the the data path of a") - print("provider shipping the 'shutter.xml' template file, usually ") - print("found under /usr/share.") - raise SystemExit() - -try: - old_profile = open(profile_path + 'shutter.xml', 'r') - content = old_profile.read() - new_profile = open(profile_path + 'shutter.xml', 'w') - # Replace the folder name with the desired one - new_profile.write(re.sub(r'folder="[^"\r\n]*"', - 'folder="%s"' % screenshot_path, content)) - new_profile.close() - old_profile.close() -except (IOError, OSError): - raise SystemExit("ERROR: While updating folder name " - "in shutter profile: {}".format(sys.exc_info())) +os.makedirs(screenshot_path, exist_ok=True) for mode in highest_modes: + message = 'Set mode ' + mode[1] + ' for output ' + mode[0] + print(message, flush=True) cmd = 'xrandr --output ' + mode[0] + ' --mode ' + mode[1] retval = subprocess.call(cmd, shell=True) if retval != 0: failures = failures + 1 message = 'Failed to set mode ' + mode[1] + ' for output ' + mode[0] - failure_messages.append(message) + print(message, file=sys.stderr, flush=True) else: - # Update shutter profile to save the image as the right name mode_string = mode[0] + '_' + mode[1] - - try: - old_profile = open(profile_path + 'shutter.xml', 'r') - content = old_profile.read() - new_profile = open(profile_path + 'shutter.xml', 'w') - new_profile.write(regex.sub('filename="%s"' % mode_string, - content)) - new_profile.close() - old_profile.close() - - shuttercmd = ['shutter', '--profile=shutter', '--full', '-e'] - retval = subprocess.call(shuttercmd, shell=False) - - if retval != 0: - print("""Could not capture screenshot - - you may need to install the package 'shutter'.""") - - except (IOError, OSError): - print("""Could not configure screenshot tool - - you may need to install the package 'shutter', - or check that {}/{} exists and is writable.""".format( - profile_path, - 'shutter.xml')) - - message = 'Set mode ' + mode[1] + ' for output ' + mode[0] - success_messages.append(message) + filename = os.path.join(screenshot_path, mode_string + '.jpg') + cmd = 'gnome-screenshot -f ' + filename + result = subprocess.run(cmd, shell=True, check=False) + if result.returncode != 0: + print("Could not capture screenshot -\n" + "you may need to install the package 'gnome-screenshot'.", + file=sys.stderr, flush=True) time.sleep(8) # let the hardware recover a bit # Put things back the way we found them @@ -226,14 +147,6 @@ try: except (IOError, OSError): pass -# Output some fun facts and knock off for the day - -for message in failure_messages: - print(message, file=sys.stderr) - -for message in success_messages: - print(message) - if failures != 0: exit(1) else: diff --git a/units/graphics/jobs.pxu b/units/graphics/jobs.pxu index 185193b..4dde304 100644 --- a/units/graphics/jobs.pxu +++ b/units/graphics/jobs.pxu @@ -230,7 +230,12 @@ depends: graphics/VESA_drivers_not_in_use command: # shellcheck disable=SC1091 source graphics_env.sh {driver} {index} - xrandr_cycle.py --screenshot-dir "$PLAINBOX_SESSION_SHARE" + if [[ $XDG_SESSION_TYPE == "wayland" ]] + then + gnome_randr_cycle.py --screenshot-dir="$PLAINBOX_SESSION_SHARE" + else + xrandr_cycle.py --screenshot-dir="$PLAINBOX_SESSION_SHARE" + fi estimated_duration: 250.000 _summary: Test resolution cycling for {vendor} {product} _description: diff --git a/units/graphics/packaging.pxu b/units/graphics/packaging.pxu new file mode 100644 index 0000000..a81bc13 --- /dev/null +++ b/units/graphics/packaging.pxu @@ -0,0 +1,8 @@ +unit: packaging meta-data +os-id: ubuntu +os-version-id: 22.04 +Depends: gnome-randr + +unit: packaging meta-data +os-id: debian +Depends: gnome-screenshot diff --git a/units/suspend/suspend-graphics.pxu b/units/suspend/suspend-graphics.pxu index 4095273..aa93645 100644 --- a/units/suspend/suspend-graphics.pxu +++ b/units/suspend/suspend-graphics.pxu @@ -93,7 +93,12 @@ estimated_duration: 120.0 command: # shellcheck disable=SC1091 source graphics_env.sh {{ driver }} {{ index }} - xrandr_cycle.py --keyword={{ index }}_after_suspend --screenshot-dir "$PLAINBOX_SESSION_SHARE" + if [[ $XDG_SESSION_TYPE == "wayland" ]] + then + gnome_randr_cycle.py --keyword={{ index }}_after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + else + xrandr_cycle.py --keyword={{ index }}_after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + fi _description: PURPOSE: This test will cycle through the detected display modes diff --git a/units/suspend/suspend.pxu b/units/suspend/suspend.pxu index 8f504fc..1225d90 100644 --- a/units/suspend/suspend.pxu +++ b/units/suspend/suspend.pxu @@ -1175,7 +1175,13 @@ id: suspend/cycle_resolutions_after_suspend estimated_duration: 120.0 requires: package.name == 'xorg' depends: suspend/suspend_advanced_auto -command: xrandr_cycle.py --keyword=after_suspend --screenshot-dir "$PLAINBOX_SESSION_SHARE" +command: + if [[ $XDG_SESSION_TYPE == "wayland" ]] + then + gnome_randr_cycle.py --keyword=after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + else + xrandr_cycle.py --keyword=after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + fi _description: PURPOSE: This test will cycle through the detected display modes @@ -1193,7 +1199,13 @@ id: suspend/{index}_cycle_resolutions_after_suspend_{product_slug} requires: package.name == 'xorg' depends: suspend/{index}_suspend_after_switch_to_card_{product_slug} estimated_duration: 120.0 -command: xrandr_cycle.py --keyword={index}_after_suspend --screenshot-dir "$PLAINBOX_SESSION_SHARE" +command: + if [[ $XDG_SESSION_TYPE == "wayland" ]] + then + gnome_randr_cycle.py --keyword={index}_after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + else + xrandr_cycle.py --keyword={index}_after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + fi _description: PURPOSE: This test will cycle through the detected display modes @@ -1211,7 +1223,13 @@ depends: suspend/suspend_advanced_auto _description: This test will check to make sure supported video modes work after a suspend and resume. This is done automatically by taking screenshots and uploading them as an attachment. -command: xrandr_cycle.py --keyword=after_suspend --screenshot-dir "$PLAINBOX_SESSION_SHARE" +command: + if [[ $XDG_SESSION_TYPE == "wayland" ]] + then + gnome_randr_cycle.py --keyword=after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + else + xrandr_cycle.py --keyword=after_suspend --screenshot-dir="$PLAINBOX_SESSION_SHARE" + fi plugin: attachment category_id: com.canonical.plainbox::suspend |