Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package switcheroo-control for openSUSE:Factory checked in at 2022-05-14 22:54:49 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/switcheroo-control (Old) and /work/SRC/openSUSE:Factory/.switcheroo-control.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "switcheroo-control" Sat May 14 22:54:49 2022 rev:11 rq:974408 version:2.5 Changes: -------- --- /work/SRC/openSUSE:Factory/switcheroo-control/switcheroo-control.changes 2021-12-13 20:51:14.552667400 +0100 +++ /work/SRC/openSUSE:Factory/.switcheroo-control.new.1538/switcheroo-control.changes 2022-05-14 22:54:56.891199825 +0200 @@ -1,0 +2,11 @@ +Sun May 1 17:31:06 UTC 2022 - Atri Bhattacharya <badshah...@gmail.com> + +- Update to version 2.5: + * Add support for setting the GPU to use for Vulkan apps, on + systems with heterogenous GPUs. + * Fix "--gpu" option not working as documented. + * Install the D-Bus configuration file in the correct location. +- Adapt dbus conf file location in keeping with upstream (requires + re-evaluation by security team bsc#1199065). + +------------------------------------------------------------------- Old: ---- switcheroo-control-2.4.tar.gz New: ---- switcheroo-control-2.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ switcheroo-control.spec ++++++ --- /var/tmp/diff_new_pack.EX9jBN/_old 2022-05-14 22:54:57.335200379 +0200 +++ /var/tmp/diff_new_pack.EX9jBN/_new 2022-05-14 22:54:57.339200384 +0200 @@ -1,7 +1,7 @@ # # spec file for package switcheroo-control # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: switcheroo-control -Version: 2.4 +Version: 2.5 Release: 0 Summary: D-Bus service to check the availability of dual-GPU License: GPL-3.0-only @@ -91,10 +91,7 @@ %{_mandir}/man1/switcherooctl.1%{?ext_man} %{_unitdir}/switcheroo-control.service %{_udevhwdbdir}/30-pci-intel-gpu.hwdb -# Own dirs to avoid depending on dbus while building. -%dir %{_sysconfdir}/dbus-1 -%dir %{_sysconfdir}/dbus-1/system.d -%config %{_sysconfdir}/dbus-1/system.d/net.hadess.SwitcherooControl.conf +%{_datadir}/dbus-1/system.d/net.hadess.SwitcherooControl.conf %files doc %doc %{_datadir}/gtk-doc/html/%{name}/ ++++++ switcheroo-control-2.4.tar.gz -> switcheroo-control-2.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/.gitlab-ci.yml new/switcheroo-control-2.5/.gitlab-ci.yml --- old/switcheroo-control-2.4/.gitlab-ci.yml 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/.gitlab-ci.yml 2022-04-29 15:47:52.000000000 +0200 @@ -13,6 +13,9 @@ python3-gobject python3-dbusmock umockdev + mesa-vulkan-drivers.x86_64 + mesa-vulkan-drivers.i686 + DIST_DEPENDENCIES: xz build: before_script: @@ -31,7 +34,7 @@ only: - tags before_script: - - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES + - dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES $DIST_DEPENDENCIES script: - meson --buildtype release -Dgtk_doc=true _build - cd _build diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/NEWS new/switcheroo-control-2.5/NEWS --- old/switcheroo-control-2.4/NEWS 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/NEWS 2022-04-29 15:47:52.000000000 +0200 @@ -1,3 +1,12 @@ +2.5 +--- + +This release adds support for setting the GPU to use for Vulkan apps, on +systems with heterogenous GPUs. + +This release also fixes the "--gpu" option not working as documented, and +installs the D-Bus configuration file in the correct location. + 2.4 --- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/data/meson.build new/switcheroo-control-2.5/data/meson.build --- old/switcheroo-control-2.4/data/meson.build 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/data/meson.build 2022-04-29 15:47:52.000000000 +0200 @@ -10,7 +10,7 @@ install_data( 'net.hadess.SwitcherooControl.conf', - install_dir: sysconfdir / 'dbus-1/system.d', + install_dir: datadir / 'dbus-1/system.d', ) install_data( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/data/switcheroo-control.service.in new/switcheroo-control-2.5/data/switcheroo-control.service.in --- old/switcheroo-control-2.4/data/switcheroo-control.service.in 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/data/switcheroo-control.service.in 2022-04-29 15:47:52.000000000 +0200 @@ -1,6 +1,6 @@ [Unit] Description=Switcheroo Control Proxy service -Before=multi-user.target display-manager.target alsa-restore.service alsa-state.service +Before=multi-user.target display-manager.service alsa-restore.service alsa-state.service [Service] Type=dbus diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/meson.build new/switcheroo-control-2.5/meson.build --- old/switcheroo-control-2.4/meson.build 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/meson.build 2022-04-29 15:47:52.000000000 +0200 @@ -1,5 +1,5 @@ project('switcheroo-control', 'c', - version : '2.4', + version : '2.5', license: 'GPLv3+', default_options : [ 'buildtype=debugoptimized', @@ -13,7 +13,7 @@ prefix = get_option('prefix') libexecdir = prefix / get_option('libexecdir') -sysconfdir = get_option('sysconfdir') +datadir = get_option('datadir') gnome = import('gnome') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/src/switcheroo-control.c new/switcheroo-control-2.5/src/switcheroo-control.c --- old/switcheroo-control-2.4/src/switcheroo-control.c 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/src/switcheroo-control.c 2022-04-29 15:47:52.000000000 +0200 @@ -231,12 +231,63 @@ return TRUE; } +static void +add_vulkan_icd_from_dir (GString *string, + const char *path, + const char *driver) +{ + GDir *dir; + const char *basename; + + dir = g_dir_open (path, 0, NULL); + if (!dir) + return; + while ((basename = g_dir_read_name (dir)) != NULL) { + g_autofree char *icd_file = NULL; + + if (strstr (basename, driver) == NULL) + continue; + icd_file = g_build_filename (path, basename, NULL); + if (string->len != 0) + g_string_append_c (string, ':'); + g_string_append (string, icd_file); + } + g_dir_close (dir); +} + +static char * +get_vulkan_icd (const char *driver) +{ + const gchar * const *dirs; + const char *vk_driver; + GString *s; + guint i; + + if (!driver) + return NULL; + + vk_driver = driver; + if (g_str_equal (driver, "i915")) + vk_driver = "intel"; + + dirs = g_get_system_data_dirs (); + s = g_string_new (NULL); + for (i = 0; dirs[i] != NULL; i++) { + g_autofree char *vk_dir = NULL; + + vk_dir = g_build_filename (dirs[i], "vulkan/icd.d", NULL); + add_vulkan_icd_from_dir (s, vk_dir, vk_driver); + } + return g_string_free (s, s->len == 0); +} + static GPtrArray * get_card_env (GUdevClient *client, GUdevDevice *dev) { GPtrArray *array; g_autoptr(GUdevDevice) parent; + char *icd_filenames; array = g_ptr_array_new_full (0, g_free); @@ -250,6 +301,10 @@ * https://download.nvidia.com/XFree86/Linux-x86_64/440.26/README/primerenderoffload.html */ g_ptr_array_add (array, g_strdup ("__NV_PRIME_RENDER_OFFLOAD")); g_ptr_array_add (array, g_strdup ("1")); + + /* Make sure Vulkan apps always select Nvidia GPUs */ + g_ptr_array_add (array, g_strdup ("__VK_LAYER_NV_optimus")); + g_ptr_array_add (array, g_strdup ("NVIDIA_only")); } else { char *id; @@ -262,6 +317,13 @@ } } + /* XXX: this doesn't work with multi-radeon or multi-nvidia setups */ + icd_filenames = get_vulkan_icd (g_udev_device_get_driver (parent)); + if (icd_filenames != NULL) { + g_ptr_array_add (array, g_strdup ("VK_ICD_FILENAMES")); + g_ptr_array_add (array, icd_filenames); + } + if (array->len == 0) { g_ptr_array_free (array, TRUE); return NULL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/src/switcherooctl.in new/switcheroo-control-2.5/src/switcherooctl.in --- old/switcheroo-control-2.4/src/switcherooctl.in 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/src/switcherooctl.in 2022-04-29 15:47:52.000000000 +0200 @@ -166,7 +166,9 @@ elif command == 'launch': if len(args) == 0: sys.exit(0) - if args[0] == '--gpu' or args[0] == '-g': + if args[0][:5] == '--gpu' or args[0] == '-g': + if args[0][:6] == '--gpu=': + args = args[0].split('=') + args[1:] if len(args) == 2: sys.exit(0) if len(args) == 1: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/tests/integration-test new/switcheroo-control-2.5/tests/integration-test --- old/switcheroo-control-2.4/tests/integration-test 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/tests/integration-test 1970-01-01 01:00:00.000000000 +0100 @@ -1,406 +0,0 @@ -#!/usr/bin/python3 - -# switcheroo-control integration test suite -# -# Run in built tree to test local built binaries, or from anywhere else to test -# system installed binaries. -# -# Copyright: (C) 2011 Martin Pitt <martin.p...@ubuntu.com> -# (C) 2020 Bastien Nocera <had...@hadess.net> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# 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. - -import os -import sys -import dbus -import tempfile -import subprocess -import unittest -import time - -try: - import gi - from gi.repository import GLib - from gi.repository import Gio -except ImportError as e: - sys.stderr.write('Skipping tests, PyGobject not available for Python 3, or missing GI typelibs: %s\n' % str(e)) - sys.exit(0) - -try: - gi.require_version('UMockdev', '1.0') - from gi.repository import UMockdev -except ImportError: - sys.stderr.write('Skipping tests, umockdev not available (https://github.com/martinpitt/umockdev)\n') - sys.exit(0) - -try: - import dbusmock -except ImportError: - sys.stderr.write('Skipping tests, python-dbusmock not available (http://pypi.python.org/pypi/python-dbusmock).\n') - sys.exit(0) - - -SC = 'net.hadess.SwitcherooControl' -SC_PATH = '/net/hadess/SwitcherooControl' - -class Tests(dbusmock.DBusTestCase): - @classmethod - def setUpClass(cls): - # run from local build tree if we are in one, otherwise use system instance - builddir = os.getenv('top_builddir', '.') - if os.access(os.path.join(builddir, 'src', 'switcheroo-control'), os.X_OK): - cls.daemon_path = os.path.join(builddir, 'src', 'switcheroo-control') - print('Testing binaries from local build tree (%s)' % cls.daemon_path) - cls.local_daemon = True - elif os.environ.get('UNDER_JHBUILD', False): - jhbuild_prefix = os.environ['JHBUILD_PREFIX'] - cls.daemon_path = os.path.join(jhbuild_prefix, 'libexec', 'switcheroo-control') - print('Testing binaries from JHBuild (%s)' % cls.daemon_path) - cls.local_daemon = False - else: - cls.daemon_path = None - with open('/usr/lib/systemd/system/switcheroo-control.service') as f: - for line in f: - if line.startswith('ExecStart='): - cls.daemon_path = line.split('=', 1)[1].strip() - break - assert cls.daemon_path, 'could not determine daemon path from systemd .service file' - cls.local_daemon = False - print('Testing installed system binary (%s)' % cls.daemon_path) - - # fail on CRITICALs on client side - GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_WARNING | - GLib.LogLevelFlags.LEVEL_ERROR | - GLib.LogLevelFlags.LEVEL_CRITICAL) - - # set up a fake system D-BUS - cls.test_bus = Gio.TestDBus.new(Gio.TestDBusFlags.NONE) - cls.test_bus.up() - try: - del os.environ['DBUS_SESSION_BUS_ADDRESS'] - except KeyError: - pass - os.environ['DBUS_SYSTEM_BUS_ADDRESS'] = cls.test_bus.get_bus_address() - - cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) - cls.dbus_con = cls.get_dbus(True) - - @classmethod - def tearDownClass(cls): - cls.test_bus.down() - dbusmock.DBusTestCase.tearDownClass() - - def setUp(self): - '''Set up a local umockdev testbed. - - The testbed is initially empty. - ''' - self.testbed = UMockdev.Testbed.new() - - self.proxy = None - self.log = None - self.daemon = None - - def tearDown(self): - del self.testbed - self.stop_daemon() - - # on failures, print daemon log - errors = [x[1] for x in self._outcome.errors if x[1]] - if errors and self.log: - with open(self.log.name) as f: - sys.stderr.write('\n-------------- daemon log: ----------------\n') - sys.stderr.write(f.read()) - sys.stderr.write('------------------------------\n') - - # - # Daemon control and D-BUS I/O - # - - def start_daemon(self): - '''Start daemon and create DBus proxy. - - When done, this sets self.proxy as the Gio.DBusProxy for switcheroo-control. - ''' - env = os.environ.copy() - env['G_DEBUG'] = 'fatal-criticals' - env['G_MESSAGES_DEBUG'] = 'all' - # note: Python doesn't propagate the setenv from Testbed.new(), so we - # have to do that ourselves - env['UMOCKDEV_DIR'] = self.testbed.get_root_dir() - self.log = tempfile.NamedTemporaryFile() - if os.getenv('VALGRIND') != None: - if self.local_daemon: - daemon_path = ['libtool', '--mode=execute', 'valgrind', self.daemon_path, '-v'] - else: - daemon_path = ['valgrind', self.daemon_path, '-v'] - else: - daemon_path = [self.daemon_path, '-v'] - - self.daemon = subprocess.Popen(daemon_path, - env=env, stdout=self.log, - stderr=subprocess.STDOUT) - - # wait until the daemon gets online - timeout = 100 - while timeout > 0: - time.sleep(0.1) - timeout -= 1 - try: - self.get_dbus_property('HasDualGpu') - break - except GLib.GError: - pass - else: - self.fail('daemon did not start in 10 seconds') - - self.proxy = Gio.DBusProxy.new_sync( - self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SC, - SC_PATH, SC, None) - - self.assertEqual(self.daemon.poll(), None, 'daemon crashed') - - def stop_daemon(self): - '''Stop the daemon if it is running.''' - - if self.daemon: - try: - self.daemon.kill() - except OSError: - pass - self.daemon.wait() - self.daemon = None - self.proxy = None - - def get_dbus_property(self, name): - '''Get property value from daemon D-Bus interface.''' - - proxy = Gio.DBusProxy.new_sync( - self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SC, - SC_PATH, 'org.freedesktop.DBus.Properties', None) - return proxy.Get('(ss)', SC, name) - - def have_text_in_log(self, text): - return self.count_text_in_log(text) > 0 - - def count_text_in_log(self, text): - with open(self.log.name) as f: - return f.read().count(text) - - def assertEventually(self, condition, message=None, timeout=50): - '''Assert that condition function eventually returns True. - - Timeout is in deciseconds, defaulting to 50 (5 seconds). message is - printed on failure. - ''' - while timeout >= 0: - context = GLib.MainContext.default() - while context.iteration(False): - pass - if condition(): - break - timeout -= 1 - time.sleep(0.1) - else: - self.fail(message or 'timed out waiting for ' + str(condition)) - - def add_intel_gpu(self): - parent = self.testbed.add_device('pci', 'i915 VGA controller', None, - [ 'boot_vga', '1' ], - [ 'DRIVER', 'i915', - 'PCI_CLASS', '30000', - 'PCI_ID', '8086:5917', - 'PCI_SUBSYS_ID', '1043:1A00' - 'PCI_SLOT_NAME', '0000:00:02.0' - 'MODALIAS', 'pci:v00008086d00005917sv00001043sd00001A00bc03sc00i00', - 'ID_PCI_CLASS_FROM_DATABASE', 'Display controller', - 'ID_PCI_SUBCLASS_FROM_DATABASE', 'VGA compatible controller', - 'ID_PCI_INTERFACE_FROM_DATABASE', 'VGA controller', - 'ID_VENDOR_FROM_DATABASE', 'Intel Corporation', - 'ID_MODEL_FROM_DATABASE', 'UHD Graphics 620', - 'SWITCHEROO_CONTROL_PRODUCT_NAME', 'UHD Graphics 620 (Kabylake GT2)', - 'SWITCHEROO_CONTROL_VENDOR_NAME', 'Intel(R)', - 'FWUPD_GUID', '0x8086:0x5917' ] - ) - - self.testbed.add_device('drm', 'dri/card0', parent, - [], - [ 'DEVNAME', '/dev/dri/card0', - 'ID_PATH', 'pci-0000:00:02.0', - 'ID_PATH_TAG', 'pci-0000_00_02_0' ] - ) - - self.testbed.add_device('drm', 'dri/renderD128', parent, - [], - [ 'DEVNAME', '/dev/dri/renderD128', - 'ID_PATH', 'pci-0000:00:02.0', - 'ID_PATH_TAG', 'pci-0000_00_02_0' ] - ) - - def add_nouveau_gpu(self): - parent = self.testbed.add_device('pci', 'NVidia VGA controller', None, - [ 'boot_vga', '0' ], - [ 'DRIVER', 'nouveau', - 'PCI_CLASS', '30200', - 'PCI_ID', '10DE:134E', - 'PCI_SUBSYS_ID', '1043:143E' - 'PCI_SLOT_NAME', '0000:01:00.0' - 'MODALIAS', 'pci:v000010DEd0000134Esv00001043sd0000143Ebc03sc02i00', - 'ID_PCI_CLASS_FROM_DATABASE', 'Display controller', - 'ID_PCI_SUBCLASS_FROM_DATABASE', '3D controller', - 'ID_PCI_INTERFACE_FROM_DATABASE', 'NVIDIA Corporation', - 'ID_MODEL_FROM_DATABASE', 'GM108M [GeForce 930MX]', - 'FWUPD_GUID', '0x10de:0x134e' ] - ) - - self.testbed.add_device('drm', 'dri/card1', parent, - [], - [ 'DEVNAME', '/dev/dri/card1', - 'ID_PATH', 'pci-0000:01:00.0', - 'ID_PATH_TAG', 'pci-0000_01_00_0' ] - ) - - self.testbed.add_device('drm', 'dri/renderD129', parent, - [], - [ 'DEVNAME', '/dev/dri/renderD129', - 'ID_PATH', 'pci-0000:01:00.0', - 'ID_PATH_TAG', 'pci-0000_01_00_0' ] - ) - - # - # Actual test cases - # - - def test_single_device(self): - '''single device''' - - self.add_intel_gpu() - - self.start_daemon() - self.assertEqual(self.get_dbus_property('HasDualGpu'), False) - self.assertEqual(self.get_dbus_property('NumGPUs'), 1) - - gpus = self.get_dbus_property('GPUs') - self.assertEqual(len(gpus), 1) - self.assertEqual(gpus[0]['Name'], 'Intel?? UHD Graphics 620 (Kabylake GT2)') - sc_env = gpus[0]['Environment'] - self.assertEqual(len(sc_env), 2) - self.assertEqual(sc_env[0], 'DRI_PRIME') - self.assertEqual(sc_env[1], 'pci-0000_00_02_0') - self.assertEqual(gpus[0]['Default'], True) - - # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) - # print (self.get_dbus_property('GPUs')) - - self.stop_daemon() - - def test_dual_open_source(self): - '''dual open source devices''' - - self.add_intel_gpu() - self.add_nouveau_gpu() - - self.start_daemon() - self.assertEqual(self.get_dbus_property('HasDualGpu'), True) - self.assertEqual(self.get_dbus_property('NumGPUs'), 2) - - gpus = self.get_dbus_property('GPUs') - self.assertEqual(len(gpus), 2) - - gpu = gpus[0] - self.assertEqual(gpu['Name'], 'GM108M [GeForce 930MX]') - sc_env = gpu['Environment'] - self.assertEqual(len(sc_env), 2) - self.assertEqual(sc_env[0], 'DRI_PRIME') - self.assertEqual(sc_env[1], 'pci-0000_01_00_0') - self.assertEqual(gpu['Default'], False) - - gpu = gpus[1] - self.assertEqual(gpu['Name'], 'Intel?? UHD Graphics 620 (Kabylake GT2)') - sc_env = gpu['Environment'] - self.assertEqual(len(sc_env), 2) - self.assertEqual(sc_env[0], 'DRI_PRIME') - self.assertEqual(sc_env[1], 'pci-0000_00_02_0') - self.assertEqual(gpu['Default'], True) - - # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) - - self.stop_daemon() - - def test_dual_open_source(self): - '''dual open source devices''' - - self.add_intel_gpu() - self.add_nouveau_gpu() - - self.testbed.add_device('drm', 'ttm', None, - [], - [ 'DEVPATH', '/devices/virtual/drm/ttm', - 'DEVTYPE', 'ttm' ] - ) - - self.start_daemon() - self.assertEqual(self.get_dbus_property('HasDualGpu'), True) - self.assertEqual(self.get_dbus_property('NumGPUs'), 2) - - gpus = self.get_dbus_property('GPUs') - self.assertEqual(len(gpus), 2) - - gpu = gpus[0] - self.assertEqual(gpu['Name'], 'GM108M [GeForce 930MX]') - self.assertEqual(gpu['Default'], False) - - gpu = gpus[1] - self.assertEqual(gpu['Name'], 'Intel?? UHD Graphics 620 (Kabylake GT2)') - self.assertEqual(gpu['Default'], True) - - # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) - - self.stop_daemon() - - def test_dual_hotplug(self): - '''dual open source devices''' - - self.add_intel_gpu() - - self.start_daemon() - self.assertEqual(self.get_dbus_property('HasDualGpu'), False) - self.assertEqual(self.get_dbus_property('NumGPUs'), 1) - - self.add_nouveau_gpu() - - self.assertEqual(self.get_dbus_property('HasDualGpu'), True) - self.assertEqual(self.get_dbus_property('NumGPUs'), 2) - - # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) - - self.stop_daemon() - - # - # Helper methods - # - - @classmethod - def _props_to_str(cls, properties): - '''Convert a properties dictionary to uevent text representation.''' - - prop_str = '' - if properties: - for k, v in properties.items(): - prop_str += '%s=%s\n' % (k, v) - return prop_str - -if __name__ == '__main__': - # run ourselves under umockdev - if 'umockdev' not in os.environ.get('LD_PRELOAD', ''): - os.execvp('umockdev-wrapper', ['umockdev-wrapper'] + sys.argv) - - unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/tests/integration-test.py new/switcheroo-control-2.5/tests/integration-test.py --- old/switcheroo-control-2.4/tests/integration-test.py 1970-01-01 01:00:00.000000000 +0100 +++ new/switcheroo-control-2.5/tests/integration-test.py 2022-04-29 15:47:52.000000000 +0200 @@ -0,0 +1,527 @@ +#!/usr/bin/python3 + +# switcheroo-control integration test suite +# +# Run in built tree to test local built binaries, or from anywhere else to test +# system installed binaries. +# +# Copyright: (C) 2011 Martin Pitt <martin.p...@ubuntu.com> +# (C) 2020 Bastien Nocera <had...@hadess.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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. + +import os +import sys +import dbus +import tempfile +import re +import subprocess +import unittest +import time + +try: + import gi + from gi.repository import GLib + from gi.repository import Gio +except ImportError as e: + sys.stderr.write('Skipping tests, PyGobject not available for Python 3, or missing GI typelibs: %s\n' % str(e)) + sys.exit(0) + +try: + gi.require_version('UMockdev', '1.0') + from gi.repository import UMockdev +except ImportError: + sys.stderr.write('Skipping tests, umockdev not available (https://github.com/martinpitt/umockdev)\n') + sys.exit(0) + +try: + import dbusmock +except ImportError: + sys.stderr.write('Skipping tests, python-dbusmock not available (http://pypi.python.org/pypi/python-dbusmock).\n') + sys.exit(0) + + +SC = 'net.hadess.SwitcherooControl' +SC_PATH = '/net/hadess/SwitcherooControl' + +class Tests(dbusmock.DBusTestCase): + @classmethod + def setUpClass(cls): + # run from local build tree if we are in one, otherwise use system instance + builddir = os.getenv('top_builddir', '.') + if os.access(os.path.join(builddir, 'src', 'switcheroo-control'), os.X_OK): + cls.daemon_path = os.path.join(builddir, 'src', 'switcheroo-control') + print('Testing binaries from local build tree (%s)' % cls.daemon_path) + cls.local_daemon = True + elif os.environ.get('UNDER_JHBUILD', False): + jhbuild_prefix = os.environ['JHBUILD_PREFIX'] + cls.daemon_path = os.path.join(jhbuild_prefix, 'libexec', 'switcheroo-control') + print('Testing binaries from JHBuild (%s)' % cls.daemon_path) + cls.local_daemon = False + else: + cls.daemon_path = None + with open('/usr/lib/systemd/system/switcheroo-control.service') as f: + for line in f: + if line.startswith('ExecStart='): + cls.daemon_path = line.split('=', 1)[1].strip() + break + assert cls.daemon_path, 'could not determine daemon path from systemd .service file' + cls.local_daemon = False + print('Testing installed system binary (%s)' % cls.daemon_path) + + # fail on CRITICALs on client side + GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_WARNING | + GLib.LogLevelFlags.LEVEL_ERROR | + GLib.LogLevelFlags.LEVEL_CRITICAL) + + # set up a fake system D-BUS + cls.test_bus = Gio.TestDBus.new(Gio.TestDBusFlags.NONE) + cls.test_bus.up() + try: + del os.environ['DBUS_SESSION_BUS_ADDRESS'] + except KeyError: + pass + os.environ['DBUS_SYSTEM_BUS_ADDRESS'] = cls.test_bus.get_bus_address() + + cls.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) + cls.dbus_con = cls.get_dbus(True) + + @classmethod + def tearDownClass(cls): + cls.test_bus.down() + dbusmock.DBusTestCase.tearDownClass() + + def setUp(self): + '''Set up a local umockdev testbed. + + The testbed is initially empty. + ''' + self.testbed = UMockdev.Testbed.new() + self.xdg_dir = self.testbed.get_root_dir() + '/share' + os.makedirs(self.xdg_dir) + + self.proxy = None + self.log = None + self.daemon = None + + def tearDown(self): + del self.testbed + self.stop_daemon() + + # on failures, print daemon log + errors = [x[1] for x in self._outcome.errors if x[1]] + if errors and self.log: + with open(self.log.name) as f: + sys.stderr.write('\n-------------- daemon log: ----------------\n') + sys.stderr.write(f.read()) + sys.stderr.write('------------------------------\n') + + # + # Daemon control and D-BUS I/O + # + + def start_daemon(self): + '''Start daemon and create DBus proxy. + + When done, this sets self.proxy as the Gio.DBusProxy for switcheroo-control. + ''' + env = os.environ.copy() + env['G_DEBUG'] = 'fatal-criticals' + env['G_MESSAGES_DEBUG'] = 'all' + # note: Python doesn't propagate the setenv from Testbed.new(), so we + # have to do that ourselves + env['UMOCKDEV_DIR'] = self.testbed.get_root_dir() + env['XDG_DATA_DIRS'] = self.xdg_dir + ':/usr/local/share/:/usr/share/' + self.log = tempfile.NamedTemporaryFile() + if os.getenv('VALGRIND') != None: + if self.local_daemon: + daemon_path = ['libtool', '--mode=execute', 'valgrind', self.daemon_path, '-v'] + else: + daemon_path = ['valgrind', self.daemon_path, '-v'] + else: + daemon_path = [self.daemon_path, '-v'] + + self.daemon = subprocess.Popen(daemon_path, + env=env, stdout=self.log, + stderr=subprocess.STDOUT) + + # wait until the daemon gets online + timeout = 100 + while timeout > 0: + time.sleep(0.1) + timeout -= 1 + try: + self.get_dbus_property('HasDualGpu') + break + except GLib.GError: + pass + else: + self.fail('daemon did not start in 10 seconds') + + self.proxy = Gio.DBusProxy.new_sync( + self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SC, + SC_PATH, SC, None) + + self.assertEqual(self.daemon.poll(), None, 'daemon crashed') + + def stop_daemon(self): + '''Stop the daemon if it is running.''' + + if self.daemon: + try: + self.daemon.kill() + except OSError: + pass + self.daemon.wait() + self.daemon = None + self.proxy = None + + def get_dbus_property(self, name): + '''Get property value from daemon D-Bus interface.''' + + proxy = Gio.DBusProxy.new_sync( + self.dbus, Gio.DBusProxyFlags.DO_NOT_AUTO_START, None, SC, + SC_PATH, 'org.freedesktop.DBus.Properties', None) + return proxy.Get('(ss)', SC, name) + + def have_text_in_log(self, text): + return self.count_text_in_log(text) > 0 + + def count_text_in_log(self, text): + with open(self.log.name) as f: + return f.read().count(text) + + def assertEventually(self, condition, message=None, timeout=50): + '''Assert that condition function eventually returns True. + + Timeout is in deciseconds, defaulting to 50 (5 seconds). message is + printed on failure. + ''' + while timeout >= 0: + context = GLib.MainContext.default() + while context.iteration(False): + pass + if condition(): + break + timeout -= 1 + time.sleep(0.1) + else: + self.fail(message or 'timed out waiting for ' + str(condition)) + + def add_intel_gpu(self): + parent = self.testbed.add_device('pci', 'i915 VGA controller', None, + [ 'boot_vga', '1' ], + [ 'DRIVER', 'i915', + 'PCI_CLASS', '30000', + 'PCI_ID', '8086:5917', + 'PCI_SUBSYS_ID', '1043:1A00' + 'PCI_SLOT_NAME', '0000:00:02.0' + 'MODALIAS', 'pci:v00008086d00005917sv00001043sd00001A00bc03sc00i00', + 'ID_PCI_CLASS_FROM_DATABASE', 'Display controller', + 'ID_PCI_SUBCLASS_FROM_DATABASE', 'VGA compatible controller', + 'ID_PCI_INTERFACE_FROM_DATABASE', 'VGA controller', + 'ID_VENDOR_FROM_DATABASE', 'Intel Corporation', + 'ID_MODEL_FROM_DATABASE', 'UHD Graphics 620', + 'SWITCHEROO_CONTROL_PRODUCT_NAME', 'UHD Graphics 620 (Kabylake GT2)', + 'SWITCHEROO_CONTROL_VENDOR_NAME', 'Intel(R)', + 'FWUPD_GUID', '0x8086:0x5917' ] + ) + + self.testbed.set_attribute_link(parent, 'driver', '../../i915') + + self.testbed.add_device('drm', 'dri/card0', parent, + [], + [ 'DEVNAME', '/dev/dri/card0', + 'ID_PATH', 'pci-0000:00:02.0', + 'ID_PATH_TAG', 'pci-0000_00_02_0' ] + ) + + self.testbed.add_device('drm', 'dri/renderD128', parent, + [], + [ 'DEVNAME', '/dev/dri/renderD128', + 'ID_PATH', 'pci-0000:00:02.0', + 'ID_PATH_TAG', 'pci-0000_00_02_0' ] + ) + + def add_nouveau_gpu(self): + parent = self.testbed.add_device('pci', 'NVidia VGA controller', None, + [ 'boot_vga', '0' ], + [ 'DRIVER', 'nouveau', + 'PCI_CLASS', '30200', + 'PCI_ID', '10DE:134E', + 'PCI_SUBSYS_ID', '1043:143E' + 'PCI_SLOT_NAME', '0000:01:00.0' + 'MODALIAS', 'pci:v000010DEd0000134Esv00001043sd0000143Ebc03sc02i00', + 'ID_PCI_CLASS_FROM_DATABASE', 'Display controller', + 'ID_PCI_SUBCLASS_FROM_DATABASE', '3D controller', + 'ID_PCI_INTERFACE_FROM_DATABASE', 'NVIDIA Corporation', + 'ID_MODEL_FROM_DATABASE', 'GM108M [GeForce 930MX]', + 'FWUPD_GUID', '0x10de:0x134e' ] + ) + + self.testbed.set_attribute_link(parent, 'driver', '../../nouveau') + + self.testbed.add_device('drm', 'dri/card1', parent, + [], + [ 'DEVNAME', '/dev/dri/card1', + 'ID_PATH', 'pci-0000:01:00.0', + 'ID_PATH_TAG', 'pci-0000_01_00_0' ] + ) + + self.testbed.add_device('drm', 'dri/renderD129', parent, + [], + [ 'DEVNAME', '/dev/dri/renderD129', + 'ID_PATH', 'pci-0000:01:00.0', + 'ID_PATH_TAG', 'pci-0000_01_00_0' ] + ) + + def add_nvidia_gpu(self): + parent = self.testbed.add_device('pci', 'NVidia VGA controller', None, + [ 'boot_vga', '0' ], + [ 'DRIVER', 'nvidia', + 'PCI_CLASS', '30000', + 'PCI_ID', '10DE:1C03', + 'PCI_SUBSYS_ID', '1043:85AC' + 'PCI_SLOT_NAME', '0000:01:00.0' + 'MODALIAS', 'pci:v000010DEd00001C03sv00001043sd000085ACbc03sc00i00', + 'ID_PCI_CLASS_FROM_DATABASE', 'Display controller', + 'ID_PCI_SUBCLASS_FROM_DATABASE', 'VGA compatible controller', + 'ID_PCI_INTERFACE_FROM_DATABASE', 'VGA controller', + 'ID_VENDOR_FROM_DATABASE', 'NVIDIA Corporation', + 'ID_MODEL_FROM_DATABASE', 'GP106 [GeForce GTX 1060 6GB]', + 'FWUPD_GUID', '0x10de:0x85ac' ] + ) + + self.testbed.set_attribute_link(parent, 'driver', '../../nvidia') + + self.testbed.add_device('drm', 'dri/card1', parent, + [], + [ 'DEVNAME', '/dev/dri/card1', + 'ID_PATH', 'pci-0000:01:00.0', + 'ID_PATH_TAG', 'pci-0000_01_00_0' ] + ) + + self.testbed.add_device('drm', 'dri/renderD129', parent, + [], + [ 'DEVNAME', '/dev/dri/renderD129', + 'ID_PATH', 'pci-0000:01:00.0', + 'ID_PATH_TAG', 'pci-0000_01_00_0' ] + ) + + # + # Actual test cases + # + + def test_single_device(self): + '''single device''' + + self.add_intel_gpu() + + self.start_daemon() + self.assertEqual(self.get_dbus_property('HasDualGpu'), False) + self.assertEqual(self.get_dbus_property('NumGPUs'), 1) + + gpus = self.get_dbus_property('GPUs') + self.assertEqual(len(gpus), 1) + self.assertEqual(gpus[0]['Name'], 'Intel?? UHD Graphics 620 (Kabylake GT2)') + sc_env = gpus[0]['Environment'] + + self.assertEqual(len(sc_env), 4) + self.assertEqual(sc_env[0], 'DRI_PRIME') + self.assertEqual(sc_env[1], 'pci-0000_00_02_0') + self.assertEqual(sc_env[2], 'VK_ICD_FILENAMES') + regex = re.compile('/usr/share/vulkan/icd\.d/intel_icd\..*json:/usr/share/vulkan/icd.d/intel_icd\..*json') + self.assertRegex(sc_env[3], regex) + self.assertEqual(gpus[0]['Default'], True) + + # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) + # print (self.get_dbus_property('GPUs')) + + self.stop_daemon() + + def test_dual_open_source(self): + '''dual open source devices''' + + self.add_intel_gpu() + self.add_nouveau_gpu() + + self.start_daemon() + self.assertEqual(self.get_dbus_property('HasDualGpu'), True) + self.assertEqual(self.get_dbus_property('NumGPUs'), 2) + + gpus = self.get_dbus_property('GPUs') + self.assertEqual(len(gpus), 2) + + gpu = gpus[0] + self.assertEqual(gpu['Name'], 'GM108M [GeForce 930MX]') + sc_env = gpu['Environment'] + self.assertEqual(len(sc_env), 2) + self.assertEqual(sc_env[0], 'DRI_PRIME') + self.assertEqual(sc_env[1], 'pci-0000_01_00_0') + self.assertEqual(gpu['Default'], False) + + gpu = gpus[1] + self.assertEqual(gpu['Name'], 'Intel?? UHD Graphics 620 (Kabylake GT2)') + sc_env = gpu['Environment'] + self.assertEqual(len(sc_env), 4) + self.assertEqual(sc_env[0], 'DRI_PRIME') + self.assertEqual(sc_env[1], 'pci-0000_00_02_0') + self.assertEqual(sc_env[2], 'VK_ICD_FILENAMES') + icds = sc_env[3].split(':') + self.assertTrue('/usr/share/vulkan/icd.d/intel_icd.x86_64.json' in icds) + self.assertTrue('/usr/share/vulkan/icd.d/intel_icd.i686.json' in icds) + self.assertEqual(gpu['Default'], True) + + # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) + + self.stop_daemon() + + def test_dual_open_source_with_ttm(self): + '''dual open source devices''' + + self.add_intel_gpu() + self.add_nouveau_gpu() + + self.testbed.add_device('drm', 'ttm', None, + [], + [ 'DEVPATH', '/devices/virtual/drm/ttm', + 'DEVTYPE', 'ttm' ] + ) + + self.start_daemon() + self.assertEqual(self.get_dbus_property('HasDualGpu'), True) + self.assertEqual(self.get_dbus_property('NumGPUs'), 2) + + gpus = self.get_dbus_property('GPUs') + self.assertEqual(len(gpus), 2) + + gpu = gpus[0] + self.assertEqual(gpu['Name'], 'GM108M [GeForce 930MX]') + self.assertEqual(gpu['Default'], False) + + gpu = gpus[1] + self.assertEqual(gpu['Name'], 'Intel?? UHD Graphics 620 (Kabylake GT2)') + self.assertEqual(gpu['Default'], True) + + # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) + + self.stop_daemon() + + def test_dual_proprietary(self): + '''oss intel + nvidia blob''' + + self.add_intel_gpu() + self.add_nvidia_gpu() + + vk_dir = os.path.join(self.xdg_dir, 'vulkan/icd.d') + os.makedirs(vk_dir) + with open(os.path.join(vk_dir, 'nvidia_icd.json'), 'w') as json: + json.write('') + + self.start_daemon() + self.assertEqual(self.get_dbus_property('HasDualGpu'), True) + self.assertEqual(self.get_dbus_property('NumGPUs'), 2) + + gpus = self.get_dbus_property('GPUs') + self.assertEqual(len(gpus), 2) + + gpu1 = gpus[0] + self.assertEqual(gpu1['Name'], 'NVIDIA Corporation GP106 [GeForce GTX 1060 6GB]') + self.assertEqual(gpu1['Default'], False) + + gpu2 = gpus[1] + self.assertEqual(gpu2['Name'], 'Intel?? UHD Graphics 620 (Kabylake GT2)') + self.assertEqual(gpu2['Default'], True) + + sc_env = gpu1['Environment'] + + self.assertIn('__GLX_VENDOR_LIBRARY_NAME', sc_env) + self.assertIn('__NV_PRIME_RENDER_OFFLOAD', sc_env) + self.assertIn('__VK_LAYER_NV_optimus', sc_env) + self.assertIn('VK_ICD_FILENAMES', sc_env) + + def get_sc_env(name): + i = sc_env.index(name) + return sc_env[i+1] + + self.assertEqual(get_sc_env('__GLX_VENDOR_LIBRARY_NAME'), 'nvidia') + self.assertEqual(get_sc_env('__NV_PRIME_RENDER_OFFLOAD'), '1') + self.assertEqual(get_sc_env('__VK_LAYER_NV_optimus'), 'NVIDIA_only') + self.assertEqual(get_sc_env('VK_ICD_FILENAMES'), vk_dir + '/nvidia_icd.json') + + self.stop_daemon() + + + def test_dual_hotplug(self): + '''dual open source devices''' + + self.add_intel_gpu() + + self.start_daemon() + self.assertEqual(self.get_dbus_property('HasDualGpu'), False) + self.assertEqual(self.get_dbus_property('NumGPUs'), 1) + + self.add_nouveau_gpu() + + self.assertEqual(self.get_dbus_property('HasDualGpu'), True) + self.assertEqual(self.get_dbus_property('NumGPUs'), 2) + + # process = subprocess.Popen(['gdbus', 'introspect', '--system', '--dest', 'net.hadess.SwitcherooControl', '--object-path', '/net/hadess/SwitcherooControl']) + + self.stop_daemon() + + def test_cmdline_tool(self): + '''test the command-line tool''' + + self.add_intel_gpu() + self.add_nouveau_gpu() + self.start_daemon() + + builddir = os.getenv('top_builddir', '.') + tool_path = os.path.join(builddir, 'src', 'switcherooctl') + + out = subprocess.run([tool_path], capture_output=True) + self.assertEqual(out.returncode, 0, "'switcherooctl' call failed") + regex = re.compile('Device: 0.*\n.*Intel.*UHD Graphics 620 \(Kabylake GT2\)\n Default: yes\n Environment: DRI_PRIME=pci-0000_00_02_0 VK_ICD_FILENAMES=/usr/share/vulkan/icd\.d/intel_icd\..*json:/usr/share/vulkan/icd.d/intel_icd\..*json\n\nDevice: 1\n Name: GM108M \[GeForce 930MX\]\n Default: no\n', re.M) + self.assertRegex(out.stdout.decode('UTF-8'), regex) + + out = subprocess.run([tool_path, 'launch', '--gpu', '0', 'env'], capture_output=True) + self.assertEqual(out.returncode, 0, "'switcherooctl launch --gpu 0' failed") + assert('DRI_PRIME=pci-0000_00_02_0' in str(out.stdout)) + + out = subprocess.run([tool_path, 'launch', '--gpu', '1', 'env'], capture_output=True) + self.assertEqual(out.returncode, 0, "'switcherooctl launch --gpu 1' failed") + assert('DRI_PRIME=pci-0000_01_00_0' in str(out.stdout)) + + out = subprocess.run([tool_path, 'launch', '--gpu=1', 'env'], capture_output=True) + self.assertEqual(out.returncode, 0, "'switcherooctl launch --gpu=1' failed") + assert('DRI_PRIME=pci-0000_01_00_0' in str(out.stdout)) + + # + # Helper methods + # + + @classmethod + def _props_to_str(cls, properties): + '''Convert a properties dictionary to uevent text representation.''' + + prop_str = '' + if properties: + for k, v in properties.items(): + prop_str += '%s=%s\n' % (k, v) + return prop_str + +if __name__ == '__main__': + # run ourselves under umockdev + if 'umockdev' not in os.environ.get('LD_PRELOAD', ''): + os.execvp('umockdev-wrapper', ['umockdev-wrapper'] + sys.argv) + + unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/tests/meson.build new/switcheroo-control-2.5/tests/meson.build --- old/switcheroo-control-2.4/tests/meson.build 2021-01-04 12:15:09.000000000 +0100 +++ new/switcheroo-control-2.5/tests/meson.build 2022-04-29 15:47:52.000000000 +0200 @@ -1,9 +1,17 @@ -integration_test = find_program('integration-test') - envs = environment() envs.set ('top_builddir', meson.build_root()) -test('switcheroo-control-integration-test', - integration_test, - env: envs - ) +python3 = find_program('python3') +unittest_inspector = find_program('unittest_inspector.py') +r = run_command(unittest_inspector, files('integration-test.py'), check: true) +unit_tests = r.stdout().strip().split('\n') + +foreach ut: unit_tests + ut_args = files('integration-test.py') + ut_args += ut + test(ut, + python3, + args: ut_args, + env: envs, + ) +endforeach diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/switcheroo-control-2.4/tests/unittest_inspector.py new/switcheroo-control-2.5/tests/unittest_inspector.py --- old/switcheroo-control-2.4/tests/unittest_inspector.py 1970-01-01 01:00:00.000000000 +0100 +++ new/switcheroo-control-2.5/tests/unittest_inspector.py 2022-04-29 15:47:52.000000000 +0200 @@ -0,0 +1,46 @@ +#! /usr/bin/env python3 +# Copyright ?? 2020, Canonical Ltd +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. +# Authors: +# Marco Trevisan <marco.trevi...@canonical.com> + +import argparse +import importlib.util +import inspect +import os +import unittest + +def list_tests(module): + tests = [] + for name, obj in inspect.getmembers(module): + if inspect.isclass(obj) and issubclass(obj, unittest.TestCase): + cases = unittest.defaultTestLoader.getTestCaseNames(obj) + tests += [ (obj, '{}.{}'.format(name, t)) for t in cases ] + return tests + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('unittest_source', type=argparse.FileType('r')) + + args = parser.parse_args() + source_path = args.unittest_source.name + spec = importlib.util.spec_from_file_location( + os.path.basename(source_path), source_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + for machine, human in list_tests(module): + print(human)