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)

Reply via email to