././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649137868.5411122 python-dbusmock-0.27.5/0000755000175100001710000000000000000000000015533 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/COPYING0000644000175100001710000001674300000000000016601 0ustar00runnerdocker00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/MANIFEST.in0000644000175100001710000000006100000000000017266 0ustar00runnerdocker00000000000000include tests/*.py include COPYING* include NEWS ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/NEWS0000644000175100001710000005001300000000000016231 0ustar00runnerdocker00000000000000## [0.27.5] - 2022-04-05 - bluez and accountsservice templates: Drop default arguments from D-Bus methods (thanks Simon McVittie) ## [0.27.4] - 2022-04-04 - Fix D-Bus signature detection regression from 0.27.0 (thanks Peter Hutterer) (#118) ## [0.27.3] - 2022-03-22 - packit: Fix file name to sync ## [0.27.2] - 2022-03-22 - packit: Really fix Fedora dist-git syncing ## [0.27.1] - 2022-03-22 - packit: Fix Fedora dist-git syncing ## [0.27.0] - 2022-03-22 - Do not register standard session service directories, add API to enable selected services (thanks Benjamin Berg) - Log static method calls from templates (thanks Peter Hutterer) ## [0.26.1] - 2022-02-25 - Fix README content type to Markdown, to fix releasing to PyPi ## [0.26.0] - 2022-02-25 - logind template: Add locking API (thanks Andy Holmes) - bluez5 template: Add RemoveDevice() and RemoveAdapterWithDevices() methods, fix properties (thanks Bastien Nocera) - Documentation improvements, particularly wrt. raising errors ## [0.25.0] - 2021-12-25 - bluez template: Implement adapter discovery, connect, disconnect, and removal (thanks Bastien Nocera) - Fix changing array properties (thanks Jonas Ådahl) - Fix CLI upower tests (thanks Marco Trevisan) - Add testing and Fedora updating through packit ## [0.24.1] - 2021-10-27 - Do not register standard D-Bus service directories (Thanks Benjamin Berg) - templates: Add IIO Sensors Proxy support (Thanks Marco Trevisan) - Fix importlib module import (Thanks Marco Trevisan) - Clean up code for most recent pylint version ## [0.24.0] - 2021-08-28 - Add Add power-profiles-daemon template (Thanks Bastien Nocera) - logind: Implement Inhibit and ListInhibitors (Thanks to Benjamin Berg) - Fix new complaints from pylint 2.9 ## [0.23.1] - 2021-06-15 - Fix tests for Python 3.10 ## [0.23.0] - 2021-03-24 - DBusMockObject: Add UpdateProperties() method (Thanks to Jonas Ådahl) - DBusTestCase: Add bus override argument to spawn_server_template, to use templates on either system or session bus; add corresponding --session CLI option - bluez template: Implement Pair() method on the Device interface and the AgentManager1 interface (Thanks to Bastien Nocera) - polkit template: Implement RegisterAuthenticationAgent() (Thanks to Jonas Ådahl) - Add accountsservice template (Thanks to Marco Trevisan) ## [0.22.0] - 2021-01-02 - NetworkManager template: Fix connecting to wifi - NetworkManager template: Add Reload() method - tests: Replace nose test runner with standard unittest - setup.py: Drop deprecated `test_suite`, run tests through `python3 -m unittest` directly ## [0.21.0] - 2021-01-01 - Add type annotations to the whole API - Drop obsolete ConsoleKit and bluez4 templates/tests - upower template: Drop support for 0.9 API ## [0.20.0] - 2020-12-19 - NetworkManager template: Handle NetworkingEnable - NetworkManager template: AddConnectionUnsaved - Drop support for Python 2 ## [0.19] - 2020-01-09 - NetworkManager template: Add "StateReason" property and active connection ID - Add low-memory-monitor template ## [0.18.3] - 2019-09-08 - Fix timeouts for loaded machines and parallel tests - Fix tests for Python 3.8 ## [0.18.2] - 2019-02-23 - test: Adjust for changed version output format in systemd 241 ## [0.18.1] - 2018-11-11 - test: Fix failures with systemd 239 in chroots ## [0.18] - 2018-07-01 - logind template: Add support for suspend-then-hibernate - NetworkManager template: Add GetConnectionByUuid method - bluez template: Add PairDevice() 'class_' parameter - Fix logging of unicode strings in Python 2 - Adjust logind and timedated tests to systemd 239 (Debian #902602) ## [0.17.2] - 2018-03-01 - NetworkManager template: Add GetDeviceByIpIface(). - Move release tarballs from Launchpad to . remains as usual. ## [0.17.1] - 2018-02-22 - Fix NetworkManager tests due to name changes from 802-11-wireless to wifi. ## [0.17] - 2017-11-07 - Call dbus-daemon directly instead of dbus-launch. (Debian #836053) - Fix DBusTestCase.stop_dbus() to actually enforce a timeout. Thanks to Renat Galimov! - Fix test_timedated for changed timedatectl output format in systemd 235. ## [0.16.9] - 2017-06-19 - NetworkManager template: Fix type of 'State' property in AddActiveConnection() - Fix BlueZ tests to work with Python 2 again. ## [0.16.8] - 2017-06-12 - bluez5 template tests: Fix failure of test_no_adapters with BlueZ 5.45. (LP: #1696480) - tests: Move from pep8 to pycodestyle for static code tests. - NetworkManager: Add ObjectManager, to work with NM ≥ 1.6. ## [0.16.7] - 2016-09-12 - bluez5 template tests: Fix failure on "Waiting to connect to bluetoothd" messages. ## [0.16.6] - 2016-06-19 - upower template tests: Fix version comparison. ## [0.16.5] - 2016-06-19 - Ofono template: generate unique ICCIDs (CardIdentifier). ## [0.16.4] - 2016-06-07 - Ofono template: Add missing properties to SimManager interface. ## [0.16.3] - 2015-12-14 - NetworkManager template test: Make the test run standalone. Thanks Pete Woods. ## [0.16.2] - 2015-12-09 - NetworkManager template: Fix connection settings Updated signal emitted by wrong object. - NetworkManager template: Handle empty device at connection activation. - NetworkManager template: Implement secrets management in settings. ## [0.16.1] - 2015-10-22 - NetworkManager template: Fix indexing bug in SettingsAddConnection. Thanks Pete Woods. ## [0.16] - 2015-10-08 - NetworkManager template: Generate a new unused name in connection activation instead of just using the access point name. Thanks Pete Woods for the original patch! - Allow the passing of template parameters via the command-line as JSON strings. Thanks Pete Woods. ## [0.15.3] - 2015-09-16 - NetworkManager template: Add missing properties to ethernet device and active connection. Thanks Pete Woods. - Quiesce irrelevant PEP-8 errors with pep8 1.6. ## [0.15.2] - 2015-06-11 - test_ofono: Test fields which don't get obfuscated with Ubuntu's latest ofono (See LP #1459983). Thanks Iain Lane. - timedated template: Add NTPSynchronized property and set it in SetNTP(), to also work with systemd 220. ## [0.15.1] - 2015-05-12 - SECURITY FIX: When loading a template from an arbitrary file through the AddTemplate() D-Bus method call or DBusTestCase.spawn_server_template() Python method, don't create or use Python's *.pyc cached files. By tricking a user into loading a template from a world-writable directory like /tmp, an attacker could run arbitrary code with the user's privileges by putting a crafted .pyc file into that directory. Note that this is highly unlikely to actually appear in practice as custom dbusmock templates are usually shipped in project directories, not directly in world-writable directories. Thanks to Simon McVittie for discovering this! (LP: #1453815, CVE-2015-1326) ## [0.15] - 2015-05-08 - NetworkManager template: Restore nm-specific PropertiesChanged signal - NetworkManager template: Add DeactivateConnection(), Settings.AddConnection(), Settings.Connection.Update(), and Settings.Connection.Delete() methods. Also allow Connections with autoconnect, added using AddConnection, to be automatically connected by the first found device, roughly like NetworkManager itself does. Thanks Jonas Grønås Drange! - NetworkManager template: Fix broken exception in AddWiFiConnection. - NetworkManager template: Set RsnFlags to have the same value as WpaFlags. Thanks Pete Woods! ## [0.14] - 2015-03-30 - Move project hosting to github, update README.rst. - urfkill template: Return boolean from block() method, as the original urfkill does. Thanks Jonas Grønås Drange! - Correctly instantiate DBusExceptions so that they get a proper name and message (issue #3) - ofono template: Fix SubscriberIdentity property type - Emit PropertiesChanged signal when Set()ing properties. - urfkill template: Return boolean from Block() and FlightMode() methods - ofono template: Add ConnectionManager interface. - NetworkManager template: Much more complete support for mocking access points and connections. ## [0.13] - 2015-02-27 - Add urfkill template. Thanks Jussi Pakkanen! ## [0.12] - 2015-01-17 - upower template: Add SetDeviceProperties() convenience method for changing upower device properties. Thanks Charles Kerr! ## [0.11.4] - 2014-09-22 - upower template: Go back to using type 's' for Device* signal arguments for upower 0.9. The older library does not get along with 'o'. (Regression in 0.11.2) ## [0.11.3] - 2014-09-19 - Fix test suite crash if loginctl is not installed. ## [0.11.2] - 2014-09-19 - upower template: Fix type of Device* signal arguments to be 'o', not 's'. Thanks Iain Lane. - ofono template: Add org.ofono.SimManager interface. Thanks Jonas Grønås Drange. - bluez5 template: Fix the type of the 'Transferred' property. Thanks Philip Withnall. - networkmanager template: Fix the "FAILED" state value to be 120, not 12. Thanks Jonas Grønås Drange. - bluez5 tests: Accept devices in state "not available" (or other states) as well. - timedated and logind tests: Adjust to also accept output format of systemd 215. ## [0.11.1] - 2014-08-08 - Rename bluez test classes to TestBlueZ4 and TestBlueZ5, to tell them apart in the output. - logind template: Fix type of IdleSinceHint property, and add IdleSinceHintMonotonic and IdleActionUSec properties. (LP: #1348437) - bluez4 template: Fix settings properties in StartDiscovery/StopDiscovery. Thanks to Mathieu Trudel-Lapierre. - NetworkManager template: Add "Devices" and "AccessPoints" properties of NetworkManager 0.9.10. (LP: #1328579) - NetworkManager template: Fix the types of the AccessPoint properties, and add some more properties. - Adjust NetworkManager template tests for changed strings in NM 0.9.10. ## [0.11] - 2014-07-24 - ofono: Fix GetOperators() to only apply to its own modem, not all modems. Thanks to Jonas Grønås Drange. - Add template for BlueZ 4. Thanks to Mathieu Trudel-Lapierre! ## [0.10.3] - 2014-07-16 - Fix upower tests to work for upower 0.99 in environments without a system D-Bus. ## [0.10.2] - 2014-07-16 - ofono: Make Scan() do the same as GetOperators(). Thanks Iain Lane. - README.rst: Clarify that the "True" argument to get_dbus() means the system bus. - Fix code to be compliant with pep8 1.5. - Fix TestCLI.test_template_system test with upower 0.99. (LP: #1330037) - ofono template: Support adding a second modem with AddModem(). (LP: #1340590) ## [0.10.1] - 2014-01-30 - Move code from bzr to git, adjust README.rst, do-release, and other files accordingly. - timedated template: Emit PropertiesChanged when setting properties. Thanks Iain Lane. ## [0.10] - 2013-12-18 - Add dynamic properties to introspection XML, to make mocked properties work with Qt's property support, d-feet, and other tools. Thanks Iain Lane! - Fix frequent KeyError() when calling static methods from a template. Regression from 0.9. - Support having the same method name on different D-Bus interfaces. - Add ofono template with support for the Manager, VoiceCallManager, and NetworkRegistration/NetworkOperator interfaces. - Add template for systemd's timedated. Thanks Iain Lane! ## [0.9.2] - 2013-12-13 - upower template: Emit DeviceAdded when adding new a new AC adapter or battery. Thanks Iain Lane! - Fix ResourceWarnings in test suite. ## [0.9.1] - 2013-12-10 - Fix UnicodeDecodeError in NEWS parsing when trying to build with a C locale. Thanks Dmitry Shachnev. ## [0.9] - 2013-11-29 - Make static template methods appear in introspection. Thanks Philip Whitnall! (LP: #1250096) - Add support for the D-Bus ObjectManager interface. This can now be enabled in templates (IS_OBJECT_MANAGER = True) or the CLI (--is-object-manager/-m). Thanks Philip Whitnall! - Add Reset() mock interface method to reset that object’s state (including removing all its sub-objects) as if the object or template had been destroyed and re-created. This is intended for use at the end of test cases to reset state before the next test case is run, as an alternative to killing python-dbusmock and re-starting it. Thanks Philip Whitnall! - logind template: Fix return value of CanSuspend() and related methods, calling them previously caused a crash. - Don't close the log file multiple times if it is shared between objects. Thanks Philip Whitnall! - Log calls to Get(), GetAll(), and Set(). - Add templates for BlueZ 5 and BlueZ-OBEX. These templates are moderately well featured, though currently targeted exclusively at testing OBEX vCard transfers from phones to computers. Thanks Philip Whitnall! ## [0.8.1] - 2013-11-11 - Fix test failure if the "upower" tool is not installed. - Fix bad upower test which assumed a locale with a comma decimal separator. ## [0.8] - 2013-11-08 - upower template: Change default daemon version to "0.9", and fix tests to work with both upower 0.9 and 1.0 client tool. - upower template: Provide 1.0 API if DaemonVersion property/template parameter gets set to >= "0.99". (LP: #1245955) - upower template: Add SetupDisplayDevice() method on the mock interface to conveniently configure all defined properties of the DisplayDevice. Only available when using the 1.0 API. (LP: #1245955) ## [0.7.2] - 2013-08-30 - Add optional "timeout" argument to DBusTestCase.wait_for_bus_object(). (LP: #1218318) - DBusTestCase.start_system_bus(): Make the fake bus look more like a real system bus by specifying a configuration file with type "system". ## [0.7.1] - 2013-08-02 - Handle the "Disconnected" signal only if it comes from the D-Bus object, to avoid accidentally reacting to other signals (like from BlueZ). Thanks Lucas De Marchi. ## [0.7] - 2013-07-30 - Accept *.py files for the--template command line option, similar to AddTemplate(). Thanks Emanuele Aina. - Pass log file to objects created with AddObject(). Thanks Emanuele Aina. - NetworkManager template: Add new method AddWiFiConnection(), to simulate a connection associated to a previously added device (with AddWiFiDevice()). Thanks Pete Woods. ## [0.6.3] - 2013-06-13 - Drop "can-suspend" and "can-hibernate" checks from upower template test. upower 1.0 deprecates this functionality, and defaults to not building it. ## [0.6.2] - 2013-06-13 - Fix test_api.TestSubclass test to work without an existing session D-Bus. ## [0.6.1] - 2013-06-13 - Add dbusmock.__version__ attribute, as per PEP-0396. - Fix not being able to inherit from DBusMockObject. Thanks Lucas De Marchi! - Allow subclassed DBusMockObject constructors to specify props arguments as None. - Fix failing notification-daemon tests for too old libnotify. (LP: #1190208) - notification_daemon template: Generate new IDs for Notify() calls with an input ID of 0. (LP: #1190209 part 1) - notification_daemon template: Send NotificationClosed signal in CloseNotification() method for valid IDs. (LP: #1190209 part 2) ## [0.6] - 2013-03-20 - Emit MethodCalled() signal on the DBusMock interface, as an alternative way of verifying method calls. Thanks Lars Uebernickel. - DBusMockObject.AddTemplate() and DBusTestCase.spawn_server_template() can now load local templates by specifying a path to a *.py file as template name. Thanks Lucas De Marchi. - Quit mock process if the D-Bus it connected to goes down. (LP: #1156561) - Support Get() and GetAll() with empty interface names, default to main interface in this case. - Add template for systemd's logind. ## [0.5] - 2013-02-03 - upower template: Change LidIsClosed property default value to False. - Add polkitd template. (LP: #1112551) ## [0.4.0] - 2013-01-21 - Add GetCalls(), GetMethodCalls() and ClearCalls() methods on the mock D-Bus interface to query the call log, for cases where parsing stdout is inconvenient. Thanks to Robert Bruce Park for the patch! (LP: #1099483) - Document how to keep internal state in AddMethod(). - Add template for gnome-screensaver. Thanks to Bastien Nocera! - Fix logging of signal arguments to have the same format as method call arguments. ## [0.3.1] - 2013-01-07 - upower template: Set Energy and EnergyFull properties. ## [0.3] - 2012-12-19 - In the logging of mock method calls, log the arguments as well. - Fix race condition in waiting for mock to get online on the bus, by avoiding triggering D-Bus service activation with system-installed services. - Add "notification_daemon" mock template. ## [0.2.2] - 2012-11-27 - tests: Suppress "nmcli and NetworkManager versions don't match" warning. - networkmanager template: Add DeviceState enum for easier specification of states. Thanks Alberto Ruiz. - Fix deprecation warnings with PyGObject 3.7.x. ## [0.2.1] - 2012-11-15 - Fix tests to skip instead of fail if NetworkManager or upower are not installed. ## [0.2.0] - 2012-11-15 - Turn dbusmock from a module into a package. This is transparent for API users, but necessary for adding future subpackages and also makes the code more maintainable. - Run pyflakes and pep8 during test suite, if available. - Add support for templates: You can now call AddTemplate() on the org.freedesktop.DBus.Mock interface to load a template into the mock, or in Python, start a server process with loading a template with DBusTestCase.spawn_server_template(). Templates set up the common structure of these services (their main objects, properties, and methods) so that you do not need to carry around this common code, and only need to set up the particular properties and specific D-Bus objects that you need. These templates can be parameterized for common customizations, and they can provide additional convenience methods on the org.freedesktop.DBus.Mock interface to provide more abstract functionality like "add a battery". - Add a first simple "upower" template with convenience methods to add AC and battery objects, and change tests/test_upower.py to use it. - Add a first "networkmanager" template with convenience methods to add Ethernet/WiFi devices and access points. Thanks Iftikhar Ahmad! - Add symbol dbusmock.MOCK_IFACE for 'org.freedesktop.DBus.Mock'. - Add test cases for command line invocation (python3-m dbusmock ...). ## [0.1.3] - 2012-11-03 - Ship NEWS in release tarballs. ## [0.1.2] - 2012-10-10 - dbusmock.py, EmitSignal(): Convert arguments to the right data types according to the signature. - dbusmock.py, method calls: Convert arguments to the right data types according to the signature. - tests/test_api.py: Check proper handling of signature vs. argument list length and/or type mismatch. (LP: #1064836) - tests/test_upower.py: Add test for handling "DeviceChanged" signal. - setup.py: Get version from NEWS file. ## [0.1.1] - 2012-10-04 - setup.py: Use README.rst as long description. - setup.py: Add download URL. - tests/test_consolekit.py: Skip test if ck-list-sessions is not installed. - setup.py: Import "multiprocessing" to work around "'NoneType' object is not callable" error when running "setup.py test" with python 2.7. ## [0.1] - 2012-10-04 - tests/test_api.py: Add tests for adding existing objects. - dbusmock.py: Also put first (main) object into the global objects map. - dbusmock.py: Document global objects map in AddMethod(). - tests/test_upower.py: Silence "No ... property" warnings; the test does not create them all. - dbusmock.py: Fix stopping of local D-Bus daemons for test subclasses. - dbusmock.py, stop_dbus(): Wait for local D-Bus to be killed, and avoid getting SIGTERMed ourselves. - dbusmock.py: Add AddProperties() method to conveniently add several properties to one interface at once. Update upower test case to use this. - dbusmock.py: Add EmitSignal() method to emit an arbitrary signal on any interface. - Make code compatible with Python 2.7 as well. (LP: #1060278) ## [0.0.3] - 2012-10-01 - tests/test_api.py: Add tests for GetAll() - wait_for_bus_object(): Poll for Introspect() instead of GetAll(), as some services such as gnome-session don't implement GetAll() properly. - Rename "dbus_mock" module to "dbusmock", to be more consistent with the project name and Python module name conventions. - Support adding properties to different interfaces. - Support adding methods to different interfaces. ## [0.0.2] - 2012-10-01 - setup.py: Add categories. ## [0.0.1] - 2012-09-28 Initial release. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649137868.5411122 python-dbusmock-0.27.5/PKG-INFO0000644000175100001710000003451400000000000016637 0ustar00runnerdocker00000000000000Metadata-Version: 2.1 Name: python-dbusmock Version: 0.27.5 Summary: Mock D-Bus objects Home-page: https://github.com/martinpitt/python-dbusmock Author: Martin Pitt Author-email: martin.pitt@ubuntu.com License: LGPL 3+ Download-URL: https://pypi.python.org/pypi/python-dbusmock/ Description: python-dbusmock =============== ## Purpose With this program/Python library you can easily create mock objects on D-Bus. This is useful for writing tests for software which talks to D-Bus services such as upower, systemd, logind, gnome-session or others, and it is hard (or impossible without root privileges) to set the state of the real services to what you expect in your tests. Suppose you want to write tests for gnome-settings-daemon's power plugin, or another program that talks to upower. You want to verify that after the configured idle time the program suspends the machine. So your program calls `org.freedesktop.UPower.Suspend()` on the system D-Bus. Now, your test suite should not really talk to the actual system D-Bus and the real upower; a `make check` that suspends your machine will not be considered very friendly by most people, and if you want to run this in continuous integration test servers or package build environments, chances are that your process does not have the privilege to suspend, or there is no system bus or upower to begin with. Likewise, there is no way for an user process to forcefully set the system/seat idle flag in logind, so your tests cannot set up the expected test environment on the real daemon. That's where mock objects come into play: They look like the real API (or at least the parts that you actually need), but they do not actually do anything (or only some action that you specify yourself). You can configure their state, behaviour and responses as you like in your test, without making any assumptions about the real system status. When using a local system/session bus, you can do unit or integration testing without needing root privileges or disturbing a running system. The Python API offers some convenience functions like `start_session_bus()` and `start_system_bus()` for this, in a `DBusTestCase` class (subclass of the standard `unittest.TestCase`). You can use this with any programming language, as you can run the mocker as a normal program. The actual setup of the mock (adding objects, methods, properties, and signals) all happen via D-Bus methods on the `org.freedesktop.DBus.Mock` interface. You just don't have the convenience D-Bus launch API that way. ## Simple example in Python Picking up the above example about mocking upower's `Suspend()` method, this is how you would set up a mock upower in your test case: ```python import dbus import dbusmock class TestMyProgram(dbusmock.DBusTestCase): @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(system_bus=True) def setUp(self): self.p_mock = self.spawn_server('org.freedesktop.UPower', '/org/freedesktop/UPower', 'org.freedesktop.UPower', system_bus=True, stdout=subprocess.PIPE) # Get a proxy for the UPower object's Mock interface self.dbus_upower_mock = dbus.Interface(self.dbus_con.get_object( 'org.freedesktop.UPower', '/org/freedesktop/UPower'), dbusmock.MOCK_IFACE) self.dbus_upower_mock.AddMethod('', 'Suspend', '', '', '') def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_suspend_on_idle(self): # run your program in a way that should trigger one suspend call # now check the log that we got one Suspend() call self.assertRegex(self.p_mock.stdout.readline(), b'^[0-9.]+ Suspend$') ``` Let's walk through: - We derive our tests from `dbusmock.DBusTestCase` instead of `unittest.TestCase` directly, to make use of the convenience API to start a local system bus. - `setUpClass()` starts a local system bus, and makes a connection to it available to all methods as `dbus_con`. `True` means that we connect to the system bus, not the session bus. We can use the same bus for all tests, so doing this once in `setUpClass()` instead of `setUp()` is enough. - `setUp()` spawns the mock D-Bus server process for an initial `/org/freedesktop/UPower` object with an `org.freedesktop.UPower` D-Bus interface on the system bus. We capture its stdout to be able to verify that methods were called. We then call `org.freedesktop.DBus.Mock.AddMethod()` to add a `Suspend()` method to our new object to the default D-Bus interface. This will not do anything (except log its call to stdout). It takes no input arguments, returns nothing, and does not run any custom code. - `tearDown()` stops our mock D-Bus server again. We do this so that each test case has a fresh and clean upower instance, but of course you can also set up everything in `setUpClass()` if tests do not interfere with each other on setting up the mock. - `test_suspend_on_idle()` is the actual test case. It needs to run your program in a way that should trigger one suspend call. Your program will try to call `Suspend()`, but as that's now being served by our mock instead of upower, there will not be any actual machine suspend. Our mock process will log the method call together with a time stamp; you can use the latter for doing timing related tests, but we just ignore it here. ## Simple example from shell We use the actual session bus for this example. You can use `dbus-run-session` to start a private one as well if you want, but that is not part of the actual mocking. So let's start a mock at the D-Bus name `com.example.Foo` with an initial "main" object on path /, with the main D-Bus interface `com.example.Foo.Manager`: python3 -m dbusmock com.example.Foo / com.example.Foo.Manager On another terminal, let's first see what it does: gdbus introspect --session -d com.example.Foo -o / You'll see that it supports the standard D-Bus `Introspectable` and `Properties` interfaces, as well as the `org.freedesktop.DBus.Mock` interface for controlling the mock, but no "real" functionality yet. So let's add a method: gdbus call --session -d com.example.Foo -o / -m org.freedesktop.DBus.Mock.AddMethod '' Ping '' '' '' Now you can see the new method in `introspect`, and call it: gdbus call --session -d com.example.Foo -o / -m com.example.Foo.Manager.Ping The mock process in the other terminal will log the method call with a time stamp, and you'll see something like `1348832614.970 Ping`. Now add another method with two int arguments and a return value and call it: gdbus call --session -d com.example.Foo -o / -m org.freedesktop.DBus.Mock.AddMethod \ '' Add 'ii' 'i' 'ret = args[0] + args[1]' gdbus call --session -d com.example.Foo -o / -m com.example.Foo.Manager.Add 2 3 This will print `(5,)` as expected (remember that the return value is always a tuple), and again the mock process will log the Add method call. You can do the same operations in e. g. d-feet or any other D-Bus language binding. ## Logging Usually you want to verify which methods have been called on the mock with which arguments. There are three ways to do that: - By default, the mock process writes the call log to stdout. - You can call the mock process with the `-l`/`--logfile` argument, or specify a log file object in the `spawn_server()` method if you are using Python. - You can use the `GetCalls()`, `GetMethodCalls()` and `ClearCalls()` methods on the `org.freedesktop.DBus.Mock` D-Bus interface to get an array of tuples describing the calls. ## Templates Some D-Bus services are commonly used in test suites, such as UPower or NetworkManager. python-dbusmock provides "templates" which set up the common structure of these services (their main objects, properties, and methods) so that you do not need to carry around this common code, and only need to set up the particular properties and specific D-Bus objects that you need. These templates can be parameterized for common customizations, and they can provide additional convenience methods on the `org.freedesktop.DBus.Mock` interface to provide more abstract functionality like "add a battery". For example, for starting a server with the `upower` template in Python you can run (self.p_mock, self.obj_upower) = self.spawn_server_template( 'upower', {'OnBattery': True}, stdout=subprocess.PIPE) or load a template into an already running server with the `AddTemplate()` method; this is particularly useful if you are not using Python: python3 -m dbusmock --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower gdbus call --system -d org.freedesktop.UPower -o /org/freedesktop/UPower -m org.freedesktop.DBus.Mock.AddTemplate 'upower' '{"OnBattery": }' This creates all expected properties such as `DaemonVersion`, and changes the default for one of them (`OnBattery`) through the (optional) parameters dict. If you do not need to specify parameters, you can do this in a simpler way with python3 -m dbusmock --template upower The template does not create any devices by default. You can add some with the template's convenience methods like ac_path = self.dbusmock.AddAC('mock_AC', 'Mock AC') bt_path = self.dbusmock.AddChargingBattery('mock_BAT', 'Mock Battery', 30.0, 1200) or calling `AddObject()` yourself with the desired properties, of course. Templates commonly implement some non-trivial functionality with actual Python methods and the standard [dbus-python](https://dbus.freedesktop.org/doc/dbus-python/) [`@dbus.service.method`](https://dbus.freedesktop.org/doc/dbus-python/dbus.service.html#dbus.service.method) decorator. If you want to contribute a template, look at dbusmock/templates/upower.py for a real-life implementation. You can copy dbusmock/templates/SKELETON to your new template file name and replace `CHANGEME` with the actual code/values. ## More Examples Have a look at the test suite for two real-live use cases: - `tests/test_upower.py` simulates upowerd, in a more complete way than in above example and using the `upower` template. It verifies that `upower --dump` is convinced that it's talking to upower. - `tests/test_api.py` runs a mock on the session bus and exercises all available functionality, such as adding additional objects, properties, multiple methods, input arguments, return values, code in methods, sending signals, raising exceptions, and introspection. ## Documentation The `dbusmock` module has extensive documentation built in, which you can read with e. g. `pydoc3 dbusmock`. `pydoc3 dbusmock.DBusMockObject` shows the D-Bus API of the mock object, i. e. methods like `AddObject()`, `AddMethod()` etc. which are used to set up your mock object. `pydoc3 dbusmock.DBusTestCase` shows the convenience Python API for writing test cases with local private session/system buses and launching the server. `pydoc3 dbusmock.templates` shows all available templates. `pydoc3 dbusmock.templates.NAME` shows the documentation and available parameters for the `NAME` template. `python3 -m dbusmock --help` shows the arguments and options for running the mock server as a program. ## Development python-dbusmock is hosted on https://github.com/martinpitt/python-dbusmock Run the unit tests with python3 -m unittest Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Other Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX :: BSD Classifier: Operating System :: Unix Classifier: Topic :: Software Development :: Quality Assurance Classifier: Topic :: Software Development :: Testing Classifier: Topic :: Software Development :: Libraries :: Python Modules Description-Content-Type: text/markdown ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/README.md0000644000175100001710000002630000000000000017013 0ustar00runnerdocker00000000000000python-dbusmock =============== ## Purpose With this program/Python library you can easily create mock objects on D-Bus. This is useful for writing tests for software which talks to D-Bus services such as upower, systemd, logind, gnome-session or others, and it is hard (or impossible without root privileges) to set the state of the real services to what you expect in your tests. Suppose you want to write tests for gnome-settings-daemon's power plugin, or another program that talks to upower. You want to verify that after the configured idle time the program suspends the machine. So your program calls `org.freedesktop.UPower.Suspend()` on the system D-Bus. Now, your test suite should not really talk to the actual system D-Bus and the real upower; a `make check` that suspends your machine will not be considered very friendly by most people, and if you want to run this in continuous integration test servers or package build environments, chances are that your process does not have the privilege to suspend, or there is no system bus or upower to begin with. Likewise, there is no way for an user process to forcefully set the system/seat idle flag in logind, so your tests cannot set up the expected test environment on the real daemon. That's where mock objects come into play: They look like the real API (or at least the parts that you actually need), but they do not actually do anything (or only some action that you specify yourself). You can configure their state, behaviour and responses as you like in your test, without making any assumptions about the real system status. When using a local system/session bus, you can do unit or integration testing without needing root privileges or disturbing a running system. The Python API offers some convenience functions like `start_session_bus()` and `start_system_bus()` for this, in a `DBusTestCase` class (subclass of the standard `unittest.TestCase`). You can use this with any programming language, as you can run the mocker as a normal program. The actual setup of the mock (adding objects, methods, properties, and signals) all happen via D-Bus methods on the `org.freedesktop.DBus.Mock` interface. You just don't have the convenience D-Bus launch API that way. ## Simple example in Python Picking up the above example about mocking upower's `Suspend()` method, this is how you would set up a mock upower in your test case: ```python import dbus import dbusmock class TestMyProgram(dbusmock.DBusTestCase): @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(system_bus=True) def setUp(self): self.p_mock = self.spawn_server('org.freedesktop.UPower', '/org/freedesktop/UPower', 'org.freedesktop.UPower', system_bus=True, stdout=subprocess.PIPE) # Get a proxy for the UPower object's Mock interface self.dbus_upower_mock = dbus.Interface(self.dbus_con.get_object( 'org.freedesktop.UPower', '/org/freedesktop/UPower'), dbusmock.MOCK_IFACE) self.dbus_upower_mock.AddMethod('', 'Suspend', '', '', '') def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_suspend_on_idle(self): # run your program in a way that should trigger one suspend call # now check the log that we got one Suspend() call self.assertRegex(self.p_mock.stdout.readline(), b'^[0-9.]+ Suspend$') ``` Let's walk through: - We derive our tests from `dbusmock.DBusTestCase` instead of `unittest.TestCase` directly, to make use of the convenience API to start a local system bus. - `setUpClass()` starts a local system bus, and makes a connection to it available to all methods as `dbus_con`. `True` means that we connect to the system bus, not the session bus. We can use the same bus for all tests, so doing this once in `setUpClass()` instead of `setUp()` is enough. - `setUp()` spawns the mock D-Bus server process for an initial `/org/freedesktop/UPower` object with an `org.freedesktop.UPower` D-Bus interface on the system bus. We capture its stdout to be able to verify that methods were called. We then call `org.freedesktop.DBus.Mock.AddMethod()` to add a `Suspend()` method to our new object to the default D-Bus interface. This will not do anything (except log its call to stdout). It takes no input arguments, returns nothing, and does not run any custom code. - `tearDown()` stops our mock D-Bus server again. We do this so that each test case has a fresh and clean upower instance, but of course you can also set up everything in `setUpClass()` if tests do not interfere with each other on setting up the mock. - `test_suspend_on_idle()` is the actual test case. It needs to run your program in a way that should trigger one suspend call. Your program will try to call `Suspend()`, but as that's now being served by our mock instead of upower, there will not be any actual machine suspend. Our mock process will log the method call together with a time stamp; you can use the latter for doing timing related tests, but we just ignore it here. ## Simple example from shell We use the actual session bus for this example. You can use `dbus-run-session` to start a private one as well if you want, but that is not part of the actual mocking. So let's start a mock at the D-Bus name `com.example.Foo` with an initial "main" object on path /, with the main D-Bus interface `com.example.Foo.Manager`: python3 -m dbusmock com.example.Foo / com.example.Foo.Manager On another terminal, let's first see what it does: gdbus introspect --session -d com.example.Foo -o / You'll see that it supports the standard D-Bus `Introspectable` and `Properties` interfaces, as well as the `org.freedesktop.DBus.Mock` interface for controlling the mock, but no "real" functionality yet. So let's add a method: gdbus call --session -d com.example.Foo -o / -m org.freedesktop.DBus.Mock.AddMethod '' Ping '' '' '' Now you can see the new method in `introspect`, and call it: gdbus call --session -d com.example.Foo -o / -m com.example.Foo.Manager.Ping The mock process in the other terminal will log the method call with a time stamp, and you'll see something like `1348832614.970 Ping`. Now add another method with two int arguments and a return value and call it: gdbus call --session -d com.example.Foo -o / -m org.freedesktop.DBus.Mock.AddMethod \ '' Add 'ii' 'i' 'ret = args[0] + args[1]' gdbus call --session -d com.example.Foo -o / -m com.example.Foo.Manager.Add 2 3 This will print `(5,)` as expected (remember that the return value is always a tuple), and again the mock process will log the Add method call. You can do the same operations in e. g. d-feet or any other D-Bus language binding. ## Logging Usually you want to verify which methods have been called on the mock with which arguments. There are three ways to do that: - By default, the mock process writes the call log to stdout. - You can call the mock process with the `-l`/`--logfile` argument, or specify a log file object in the `spawn_server()` method if you are using Python. - You can use the `GetCalls()`, `GetMethodCalls()` and `ClearCalls()` methods on the `org.freedesktop.DBus.Mock` D-Bus interface to get an array of tuples describing the calls. ## Templates Some D-Bus services are commonly used in test suites, such as UPower or NetworkManager. python-dbusmock provides "templates" which set up the common structure of these services (their main objects, properties, and methods) so that you do not need to carry around this common code, and only need to set up the particular properties and specific D-Bus objects that you need. These templates can be parameterized for common customizations, and they can provide additional convenience methods on the `org.freedesktop.DBus.Mock` interface to provide more abstract functionality like "add a battery". For example, for starting a server with the `upower` template in Python you can run (self.p_mock, self.obj_upower) = self.spawn_server_template( 'upower', {'OnBattery': True}, stdout=subprocess.PIPE) or load a template into an already running server with the `AddTemplate()` method; this is particularly useful if you are not using Python: python3 -m dbusmock --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower gdbus call --system -d org.freedesktop.UPower -o /org/freedesktop/UPower -m org.freedesktop.DBus.Mock.AddTemplate 'upower' '{"OnBattery": }' This creates all expected properties such as `DaemonVersion`, and changes the default for one of them (`OnBattery`) through the (optional) parameters dict. If you do not need to specify parameters, you can do this in a simpler way with python3 -m dbusmock --template upower The template does not create any devices by default. You can add some with the template's convenience methods like ac_path = self.dbusmock.AddAC('mock_AC', 'Mock AC') bt_path = self.dbusmock.AddChargingBattery('mock_BAT', 'Mock Battery', 30.0, 1200) or calling `AddObject()` yourself with the desired properties, of course. Templates commonly implement some non-trivial functionality with actual Python methods and the standard [dbus-python](https://dbus.freedesktop.org/doc/dbus-python/) [`@dbus.service.method`](https://dbus.freedesktop.org/doc/dbus-python/dbus.service.html#dbus.service.method) decorator. If you want to contribute a template, look at dbusmock/templates/upower.py for a real-life implementation. You can copy dbusmock/templates/SKELETON to your new template file name and replace `CHANGEME` with the actual code/values. ## More Examples Have a look at the test suite for two real-live use cases: - `tests/test_upower.py` simulates upowerd, in a more complete way than in above example and using the `upower` template. It verifies that `upower --dump` is convinced that it's talking to upower. - `tests/test_api.py` runs a mock on the session bus and exercises all available functionality, such as adding additional objects, properties, multiple methods, input arguments, return values, code in methods, sending signals, raising exceptions, and introspection. ## Documentation The `dbusmock` module has extensive documentation built in, which you can read with e. g. `pydoc3 dbusmock`. `pydoc3 dbusmock.DBusMockObject` shows the D-Bus API of the mock object, i. e. methods like `AddObject()`, `AddMethod()` etc. which are used to set up your mock object. `pydoc3 dbusmock.DBusTestCase` shows the convenience Python API for writing test cases with local private session/system buses and launching the server. `pydoc3 dbusmock.templates` shows all available templates. `pydoc3 dbusmock.templates.NAME` shows the documentation and available parameters for the `NAME` template. `python3 -m dbusmock --help` shows the arguments and options for running the mock server as a program. ## Development python-dbusmock is hosted on https://github.com/martinpitt/python-dbusmock Run the unit tests with python3 -m unittest ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649137868.5331123 python-dbusmock-0.27.5/dbusmock/0000755000175100001710000000000000000000000017342 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/__init__.py0000644000175100001710000000143300000000000021454 0ustar00runnerdocker00000000000000# coding: UTF-8 '''Mock D-Bus objects for test suites.''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' __version__ = '0.27.5' from dbusmock.mockobject import (DBusMockObject, MOCK_IFACE, OBJECT_MANAGER_IFACE, get_object, get_objects) from dbusmock.testcase import DBusTestCase __all__ = ['DBusMockObject', 'MOCK_IFACE', 'OBJECT_MANAGER_IFACE', 'DBusTestCase', 'get_object', 'get_objects'] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/__main__.py0000644000175100001710000001155100000000000021437 0ustar00runnerdocker00000000000000# coding: UTF-8 '''Main entry point for running mock server.''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import argparse import json import sys import dbusmock.mockobject import dbusmock.testcase def parse_args(): '''Parse command line arguments''' parser = argparse.ArgumentParser(description='mock D-Bus object') parser.add_argument('-s', '--system', action='store_true', help="put object(s) on system bus (default: session bus or template's SYSTEM_BUS flag)") parser.add_argument('--session', action='store_true', help="put object(s) on session bus (default without template; overrides template's SYSTEM_BUS flag)") parser.add_argument('-l', '--logfile', metavar='PATH', help='path of log file') parser.add_argument('-t', '--template', metavar='NAME', help='template to load (instead of specifying name, path, interface)') parser.add_argument('name', metavar='NAME', nargs='?', help='D-Bus name to claim (e. g. "com.example.MyService") (if not using -t)') parser.add_argument('path', metavar='PATH', nargs='?', help='D-Bus object path for initial/main object (if not using -t)') parser.add_argument('interface', metavar='INTERFACE', nargs='?', help='main D-Bus interface name for initial object (if not using -t)') parser.add_argument('-m', '--is-object-manager', action='store_true', help='automatically implement the org.freedesktop.DBus.ObjectManager interface') parser.add_argument('-p', '--parameters', help='JSON dictionary of parameters to pass to the template') arguments = parser.parse_args() if arguments.template: if arguments.name or arguments.path or arguments.interface: parser.error('--template and specifying NAME/PATH/INTERFACE are mutually exclusive') else: if not arguments.name or not arguments.path or not arguments.interface: parser.error('Not using a template, you must specify NAME, PATH, and INTERFACE') if arguments.system and arguments.session: parser.error('--system and --session are mutually exclusive') return arguments if __name__ == '__main__': import dbus.service import dbus.mainloop.glib from gi.repository import GLib args = parse_args() dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) system_bus = args.system if args.template: module = dbusmock.mockobject.load_module(args.template) args.name = module.BUS_NAME args.path = module.MAIN_OBJ if not args.session and not args.system: system_bus = module.SYSTEM_BUS if hasattr(module, 'IS_OBJECT_MANAGER'): args.is_object_manager = module.IS_OBJECT_MANAGER else: args.is_object_manager = False if args.is_object_manager and not hasattr(module, 'MAIN_IFACE'): args.interface = dbusmock.mockobject.OBJECT_MANAGER_IFACE else: args.interface = module.MAIN_IFACE main_loop = GLib.MainLoop() bus = dbusmock.testcase.DBusTestCase.get_dbus(system_bus) # quit mock when the bus is going down bus.add_signal_receiver(main_loop.quit, signal_name='Disconnected', path='/org/freedesktop/DBus/Local', dbus_interface='org.freedesktop.DBus.Local') bus_name = dbus.service.BusName(args.name, bus, allow_replacement=True, replace_existing=True, do_not_queue=True) main_object = dbusmock.mockobject.DBusMockObject(bus_name, args.path, args.interface, {}, args.logfile, args.is_object_manager) parameters = None if args.parameters: try: parameters = json.loads(args.parameters) except ValueError as detail: sys.stderr.write(f'Malformed JSON given for parameters: {detail}\n') sys.exit(2) if not isinstance(parameters, dict): sys.stderr.write('JSON parameters must be a dictionary\n') sys.exit(2) if args.template: main_object.AddTemplate(args.template, parameters) dbusmock.mockobject.objects[args.path] = main_object main_loop.run() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/mockobject.py0000644000175100001710000010166500000000000022045 0ustar00runnerdocker00000000000000# coding: UTF-8 '''Mock D-Bus objects for test suites.''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import copy import functools import importlib import importlib.util import os import sys import time import types from typing import Optional, Dict, Any, List, Tuple, Sequence, KeysView from xml.etree import ElementTree import dbus import dbus.service # we do not use this ourselves, but mock methods often want to use this os # pyflakes pylint: disable=pointless-statement # global path -> DBusMockObject mapping objects: Dict[str, 'DBusMockObject'] = {} MOCK_IFACE = 'org.freedesktop.DBus.Mock' OBJECT_MANAGER_IFACE = 'org.freedesktop.DBus.ObjectManager' PropsType = Dict[str, Any] # (in_signature, out_signature, code, dbus_wrapper_fn) MethodType = Tuple[str, str, str, str] # (timestamp, method_name, call_args) CallLogType = Tuple[int, str, Sequence[Any]] def load_module(name: str): '''Load a mock template Python module from dbusmock/templates/''' if os.path.exists(name) and os.path.splitext(name)[1] == '.py': spec = importlib.util.spec_from_file_location(os.path.splitext(os.path.basename(name))[0], name) assert spec mod = importlib.util.module_from_spec(spec) with open(name, encoding="UTF-8") as f: exec(f.read(), mod.__dict__, mod.__dict__) # pylint: disable=exec-used return mod return importlib.import_module('dbusmock.templates.' + name) def _format_args(args): '''Format a D-Bus argument tuple into an appropriate logging string''' def format_arg(a): if isinstance(a, dbus.Boolean): return str(bool(a)) if isinstance(a, (dbus.Byte, int)): return str(int(a)) if isinstance(a, str): return '"' + str(a) + '"' if isinstance(a, list): return '[' + ', '.join([format_arg(x) for x in a]) + ']' if isinstance(a, dict): fmta = '{' first = True for k, v in a.items(): if first: first = False else: fmta += ', ' fmta += format_arg(k) + ': ' + format_arg(v) return fmta + '}' # fallback return repr(a) s = '' for a in args: if s: s += ' ' s += format_arg(a) if s: s = ' ' + s return s def _wrap_in_dbus_variant(value): dbus_types = [ dbus.types.ByteArray, dbus.types.Int16, dbus.types.ObjectPath, dbus.types.Struct, dbus.types.UInt64, dbus.types.Boolean, dbus.types.Dictionary, dbus.types.Int32, dbus.types.Signature, dbus.types.UInt16, dbus.types.UnixFd, dbus.types.Byte, dbus.types.Double, dbus.types.Int64, dbus.types.String, dbus.types.UInt32, ] if isinstance(value, dbus.String): return dbus.String(str(value), variant_level=1) if isinstance(value, dbus.types.Array): return value if type(value) in dbus_types: return type(value)(value.conjugate(), variant_level=1) if isinstance(value, str): return dbus.String(value, variant_level=1) raise dbus.exceptions.DBusException(f'could not wrap type {type(value)}') def _convert_args(signature: str, args: Tuple[Any, ...]) -> List[Any]: """ Convert types of arguments according to signature, using MethodCallMessage.append(); this will also provide type/length checks, except for the case of an empty signature """ try: if signature == '' and len(args) > 0: raise TypeError('Fewer items found in D-Bus signature than in Python arguments') m = dbus.connection.MethodCallMessage('a.b', '/a', 'a.b', 'a') m.append(signature=signature, *args) return m.get_args_list() except Exception as e: raise dbus.exceptions.DBusException(f'Invalid arguments: {str(e)}', name='org.freedesktop.DBus.Error.InvalidArgs') def loggedmethod(self, func): """Decorator for a method to end in the call log""" @functools.wraps(func) def wrapper(*args, **kwargs): fname = func.__name__ self_arg, args = args[0], args[1:] in_signature = getattr(func, '_dbus_in_signature', '') args = _convert_args(in_signature, args) self.log(fname + _format_args(args)) self.call_log.append((int(time.time()), fname, args)) self.MethodCalled(fname, args) return func(*[self_arg, *args], **kwargs) return wrapper class DBusMockObject(dbus.service.Object): # pylint: disable=too-many-instance-attributes '''Mock D-Bus object This can be configured to have arbitrary methods (including code execution) and properties via methods on the org.freedesktop.DBus.Mock interface, so that you can control the mock from any programming language. Beyond that "remote control" API, this is a standard dbus-python service object, see . ''' def __init__(self, bus_name: str, path: str, interface: str, props: PropsType, logfile: Optional[str] = None, is_object_manager: bool = False) -> None: '''Create a new DBusMockObject bus_name: A dbus.service.BusName instance where the object will be put on path: D-Bus object path interface: Primary D-Bus interface name of this object (where properties and methods will be put on) props: A property_name (string) → property (Variant) map with initial properties on "interface" logfile: When given, method calls will be logged into that file name; if None, logging will be written to stdout. Note that you can also query the called methods over D-Bus with GetCalls() and GetMethodCalls(). is_object_manager: If True, the GetManagedObjects method will automatically be implemented on the object, returning all objects which have this one’s path as a prefix of theirs. Note that the InterfacesAdded and InterfacesRemoved signals will not be automatically emitted. ''' dbus.service.Object.__init__(self, bus_name, path) self.bus_name = bus_name self.path = path self.interface = interface self.is_object_manager = is_object_manager self.object_manager: Optional[DBusMockObject] = None self._template: Optional[str] = None self._template_parameters: Optional[PropsType] = None # pylint: disable=consider-using-with self.logfile = open(logfile, 'wb') if logfile else None self.is_logfile_owner = True self.call_log: List[CallLogType] = [] if props is None: props = {} self._reset(props) def __del__(self) -> None: try: if self.logfile and self.is_logfile_owner: self.logfile.close() except AttributeError: pass def _set_up_object_manager(self) -> None: '''Set up this mock object as a D-Bus ObjectManager.''' if self.path == '/': cond = 'k != \'/\'' else: cond = f'k.startswith(\'{self.path}/\')' self.AddMethod(OBJECT_MANAGER_IFACE, 'GetManagedObjects', '', 'a{oa{sa{sv}}}', 'ret = {dbus.ObjectPath(k): objects[k].props ' + ' for k in objects.keys() if ' + cond + '}') self.object_manager = self def _reset(self, props: PropsType) -> None: # interface -> name -> value self.props = {self.interface: props} # interface -> name -> (in_signature, out_signature, code, dbus_wrapper_fn) self.methods: Dict[str, Dict[str, MethodType]] = {self.interface: {}} if self.is_object_manager: self._set_up_object_manager() @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') def Get(self, interface_name: str, property_name: str) -> Any: '''Standard D-Bus API for getting a property value''' self.log(f'Get {self.path} {interface_name}.{property_name}') if not interface_name: interface_name = self.interface try: return self.GetAll(interface_name)[property_name] except KeyError as e: raise dbus.exceptions.DBusException( 'no such property ' + property_name, name=self.interface + '.UnknownProperty') from e @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') def GetAll(self, interface_name: str, *_, **__) -> PropsType: '''Standard D-Bus API for getting all property values''' self.log(f'GetAll {self.path} {interface_name}') if not interface_name: interface_name = self.interface try: return self.props[interface_name] except KeyError as e: raise dbus.exceptions.DBusException( 'no such interface ' + interface_name, name=self.interface + '.UnknownInterface') from e @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='') def Set(self, interface_name: str, property_name: str, value: Any, *_, **__) -> None: '''Standard D-Bus API for setting a property value''' self.log(f'Set {self.path} {interface_name}.{property_name}{_format_args((value,))}') try: iface_props = self.props[interface_name] except KeyError as e: raise dbus.exceptions.DBusException( 'no such interface ' + interface_name, name=self.interface + '.UnknownInterface') from e if property_name not in iface_props: raise dbus.exceptions.DBusException( 'no such property ' + property_name, name=self.interface + '.UnknownProperty') iface_props[property_name] = value self.EmitSignal('org.freedesktop.DBus.Properties', 'PropertiesChanged', 'sa{sv}as', [interface_name, dbus.Dictionary({property_name: value}, signature='sv'), dbus.Array([], signature='s') ]) @dbus.service.method(MOCK_IFACE, in_signature='ssa{sv}a(ssss)', out_signature='') def AddObject(self, path: str, interface: str, properties: PropsType, methods: List[MethodType]) -> None: '''Dynamically add a new D-Bus object to the mock path: D-Bus object path interface: Primary D-Bus interface name of this object (where properties and methods will be put on) properties: A property_name (string) → value map with initial properties on "interface" methods: An array of 4-tuples (name, in_sig, out_sig, code) describing methods to add to "interface"; see AddMethod() for details of the tuple values If this is a D-Bus ObjectManager instance, the InterfacesAdded signal will *not* be emitted for the object automatically; it must be emitted manually if desired. This is because AddInterface may be called after AddObject, but before the InterfacesAdded signal should be emitted. Example: dbus_proxy.AddObject('/com/example/Foo/Manager', 'com.example.Foo.Control', { 'state': dbus.String('online', variant_level=1), }, [ ('Start', '', '', ''), ('EchoInt', 'i', 'i', 'ret = args[0]'), ('GetClients', '', 'ao', 'ret = ["/com/example/Foo/Client1"]'), ]) ''' if path in objects: raise dbus.exceptions.DBusException(f'object {path} already exists', name='org.freedesktop.DBus.Mock.NameError') obj = DBusMockObject(self.bus_name, path, interface, properties) # make sure created objects inherit the log file stream obj.logfile = self.logfile obj.object_manager = self.object_manager obj.is_logfile_owner = False obj.AddMethods(interface, methods) objects[path] = obj @dbus.service.method(MOCK_IFACE, in_signature='s', out_signature='') def RemoveObject(self, path: str) -> None: # pylint: disable=no-self-use '''Remove a D-Bus object from the mock As with AddObject, this will *not* emit the InterfacesRemoved signal if it’s an ObjectManager instance. ''' try: objects[path].remove_from_connection() del objects[path] except KeyError as e: raise dbus.exceptions.DBusException( f'object {path} does not exist', name='org.freedesktop.DBus.Mock.NameError') from e @dbus.service.method(MOCK_IFACE, in_signature='', out_signature='') def Reset(self) -> None: '''Reset the mock object state. Remove all mock objects from the bus and tidy up so the state is as if python-dbusmock had just been restarted. If the mock object was originally created with a template (from the command line, the Python API or by calling AddTemplate over D-Bus), it will be re-instantiated with that template. ''' # Clear other existing objects. for obj_name, obj in objects.items(): if obj_name != self.path: obj.remove_from_connection() objects.clear() # Reinitialise our state. Carefully remove new methods from our dict; # they don't not actually exist if they are a statically defined # template function for method_name in self.methods[self.interface]: try: delattr(self.__class__, method_name) except AttributeError: pass self._reset({}) if self._template is not None: self.AddTemplate(self._template, self._template_parameters) objects[self.path] = self @dbus.service.method(MOCK_IFACE, in_signature='sssss', out_signature='') def AddMethod(self, interface, name: str, in_sig: str, out_sig: str, code: str) -> None: '''Dynamically add a method to this object interface: D-Bus interface to add this to. For convenience you can specify '' here to add the method to the object's main interface (as specified on construction). name: Name of the method in_sig: Signature of input arguments; for example "ias" for a method that takes an int32 and a string array as arguments; see http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures out_sig: Signature of output arguments; for example "s" for a method that returns a string; use '' for methods that do not return anything. code: Python 3 code to run in the method call; you have access to the arguments through the "args" list, and can set the return value by assigning a value to the "ret" variable. You can also read the global "objects" variable, which is a dictionary mapping object paths to DBusMockObject instances. For keeping state across method calls, you are free to use normal Python members of the "self" object, which will be persistent for the whole mock's life time. E. g. you can have a method with "self.my_state = True", and another method that returns it with "ret = self.my_state". Methods can raise exceptions in the usual way, in particular dbus.exceptions.DBusException: When specifying '', the method will not do anything (except logging) and return None. This is meant for adding a method to a mock at runtime, from any programming language. You can also use it in templates in the load() function. For implementing non-trivial and static methods in templates, it is recommended to implement them in the normal dbus-python way with using the @dbus.service.method decorator instead. ''' # pylint: disable=protected-access if not interface: interface = self.interface n_args = len(dbus.Signature(in_sig)) # we need to have separate methods for dbus-python, so clone # mock_method(); using message_keyword with this dynamic approach fails # because inspect cannot handle those, so pass on interface and method # name as first positional arguments method = lambda self, *args, **kwargs: DBusMockObject.mock_method( self, interface, name, in_sig, *args, **kwargs) # we cannot specify in_signature here, as that trips over a consistency # check in dbus-python; we need to set it manually instead dbus_method = dbus.service.method(interface, out_signature=out_sig)(method) dbus_method.__name__ = str(name) dbus_method._dbus_in_signature = in_sig dbus_method._dbus_args = [f'arg{i}' for i in range(1, n_args + 1)] # for convenience, add mocked methods on the primary interface as # callable methods if interface == self.interface: setattr(self.__class__, name, dbus_method) self.methods.setdefault(interface, {})[str(name)] = (in_sig, out_sig, code, dbus_method) @dbus.service.method(MOCK_IFACE, in_signature='sa(ssss)', out_signature='') def AddMethods(self, interface: str, methods: List[MethodType]) -> None: '''Add several methods to this object interface: D-Bus interface to add this to. For convenience you can specify '' here to add the method to the object's main interface (as specified on construction). methods: list of 4-tuples (name, in_sig, out_sig, code) describing one method each. See AddMethod() for details of the tuple values. ''' for method in methods: self.AddMethod(interface, *method) def _set_property(self, interface, name, value): # copy.copy removes one level of variant-ness, which means that the # types get exported in introspection data correctly, but we can't do # this for container types. if not isinstance(value, (dbus.Dictionary, dbus.Array)): value = copy.copy(value) self.props.setdefault(interface, {})[name] = value @dbus.service.method(MOCK_IFACE, in_signature='sa{sv}', out_signature='') def UpdateProperties(self, interface: str, properties: PropsType) -> None: '''Update properties on this object and send a PropertiesChanged signal interface: D-Bus interface to update this to. For convenience you can specify '' here to add the property to the object's main interface (as specified on construction). properties: A property_name (string) → value map ''' changed_props = {} for name, value in properties.items(): if not interface: interface = self.interface if name not in self.props.get(interface, {}): raise dbus.exceptions.DBusException(f'property {name} not found', name=interface + '.NoSuchProperty') self._set_property(interface, name, value) changed_props[name] = _wrap_in_dbus_variant(value) self.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ interface, changed_props, []]) @dbus.service.method(MOCK_IFACE, in_signature='ssv', out_signature='') def AddProperty(self, interface: str, name: str, value: Any) -> None: '''Add property to this object interface: D-Bus interface to add this to. For convenience you can specify '' here to add the property to the object's main interface (as specified on construction). name: Property name. value: Property value. ''' if not interface: interface = self.interface if name in self.props.get(interface, {}): raise dbus.exceptions.DBusException(f'property {name} already exists', name=self.interface + '.PropertyExists') self._set_property(interface, name, value) @dbus.service.method(MOCK_IFACE, in_signature='sa{sv}', out_signature='') def AddProperties(self, interface: str, properties: PropsType) -> None: '''Add several properties to this object interface: D-Bus interface to add this to. For convenience you can specify '' here to add the property to the object's main interface (as specified on construction). properties: A property_name (string) → value map ''' for k, v in properties.items(): self.AddProperty(interface, k, v) @dbus.service.method(MOCK_IFACE, in_signature='sa{sv}', out_signature='') def AddTemplate(self, template: str, parameters: PropsType) -> None: '''Load a template into the mock. python-dbusmock ships a set of standard mocks for common system services such as UPower and NetworkManager. With these the actual tests become a lot simpler, as they only have to set up the particular properties for the tests, and not the skeleton of common properties, interfaces, and methods. template: Name of the template to load or the full path to a *.py file for custom templates. See "pydoc dbusmock.templates" for a list of available templates from python-dbusmock package, and "pydoc dbusmock.templates.NAME" for documentation about template NAME. parameters: A parameter (string) → value (variant) map, for parameterizing templates. Each template can define their own, see documentation of that particular template for details. ''' try: module = load_module(template) except ImportError as e: raise dbus.exceptions.DBusException(f'Cannot add template {template}: {str(e)}', name='org.freedesktop.DBus.Mock.TemplateError') # If the template specifies this is an ObjectManager, set that up if hasattr(module, 'IS_OBJECT_MANAGER') and module.IS_OBJECT_MANAGER: self._set_up_object_manager() # pick out all D-Bus service methods and add them to our interface for symbol in dir(module): # pylint: disable=protected-access fn = getattr(module, symbol) if ('_dbus_interface' in dir(fn) and ('_dbus_is_signal' not in dir(fn) or not fn._dbus_is_signal)): fn = loggedmethod(self, fn) # for dbus-python compatibility, add methods as callables setattr(self.__class__, symbol, fn) self.methods.setdefault(fn._dbus_interface, {})[str(symbol)] = ( fn._dbus_in_signature, fn._dbus_out_signature, '', fn ) if parameters is None: parameters = {} module.load(self, parameters) # save the given template and parameters for re-instantiation on # Reset() self._template = template self._template_parameters = parameters @dbus.service.method(MOCK_IFACE, in_signature='sssav', out_signature='') def EmitSignal(self, interface: str, name: str, signature: str, sigargs: Tuple[Any, ...]) -> None: '''Emit a signal from the object. interface: D-Bus interface to send the signal from. For convenience you can specify '' here to add the method to the object's main interface (as specified on construction). name: Name of the signal signature: Signature of input arguments; for example "ias" for a signal that takes an int32 and a string array as arguments; see http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures args: variant array with signal arguments; must match order and type in "signature" ''' # pylint: disable=protected-access if not interface: interface = self.interface args = _convert_args(signature, sigargs) fn = lambda self, *args: self.log(f'emit {self.path} {interface}.{name}{_format_args(args)}') fn.__name__ = str(name) dbus_fn = dbus.service.signal(interface)(fn) dbus_fn._dbus_signature = signature dbus_fn._dbus_args = [f'arg{i}' for i in range(1, len(args) + 1)] dbus_fn(self, *args) @dbus.service.method(MOCK_IFACE, in_signature='', out_signature='a(tsav)') def GetCalls(self) -> List[CallLogType]: '''List all the logged calls since the last call to ClearCalls(). Return a list of (timestamp, method_name, args_list) tuples. ''' return self.call_log @dbus.service.method(MOCK_IFACE, in_signature='s', out_signature='a(tav)') def GetMethodCalls(self, method: str) -> List[Tuple[int, Sequence[Any]]]: '''List all the logged calls of a particular method. Return a list of (timestamp, args_list) tuples. ''' return [(row[0], row[2]) for row in self.call_log if row[1] == method] @dbus.service.method(MOCK_IFACE, in_signature='', out_signature='') def ClearCalls(self) -> None: '''Empty the log of mock call signatures.''' self.call_log = [] @dbus.service.signal(MOCK_IFACE, signature='sav') def MethodCalled(self, name, args): '''Signal emitted for every called mock method. This is emitted for all mock method calls. This can be used to confirm that a particular method was called with particular arguments, as an alternative to reading the mock's log or GetCalls(). ''' def object_manager_emit_added(self, path: str) -> None: '''Emit ObjectManager.InterfacesAdded signal''' if self.object_manager is not None: self.object_manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesAdded', 'oa{sa{sv}}', [dbus.ObjectPath(path), objects[path].props]) def object_manager_emit_removed(self, path: str) -> None: '''Emit ObjectManager.InterfacesRemoved signal''' if self.object_manager is not None: self.object_manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesRemoved', 'oas', [dbus.ObjectPath(path), objects[path].props]) def mock_method(self, interface: str, dbus_method: str, in_signature: str, *m_args, **_) -> Any: '''Master mock method. This gets "instantiated" in AddMethod(). Execute the code snippet of the method and return the "ret" variable if it was set. ''' # print('mock_method', dbus_method, self, in_signature, args, _, file=sys.stderr) try: args = _convert_args(in_signature, m_args) self.log(dbus_method + _format_args(args)) self.call_log.append((int(time.time()), str(dbus_method), args)) self.MethodCalled(dbus_method, args) # The code may be a Python 3 string to interpret, or may be a function # object (if AddMethod was called from within Python itself, rather than # over D-Bus). code = self.methods[interface][dbus_method][2] if code and isinstance(code, types.FunctionType): return code(self, *args) if code: loc = locals().copy() exec(code, globals(), loc) # pylint: disable=exec-used if 'ret' in loc: return loc['ret'] except Exception as e: self.log(dbus_method + ' raised: ' + str(e)) raise e return None def log(self, msg: str) -> None: '''Log a message, prefixed with a timestamp. If a log file was specified in the constructor, it is written there, otherwise it goes to stdout. ''' if self.logfile: fd = self.logfile.fileno() else: fd = sys.stdout.fileno() os.write(fd, f'{time.time():.3f} {msg}\n'.encode('UTF-8')) @dbus.service.method(dbus.INTROSPECTABLE_IFACE, in_signature='', out_signature='s', path_keyword='object_path', connection_keyword='connection') def Introspect(self, object_path: str, connection: dbus.connection.Connection) -> str: '''Return XML description of this object's interfaces, methods and signals. This wraps dbus-python's Introspect() method to include the dynamic methods and properties. ''' # _dbus_class_table is an indirect private member of dbus.service.Object that pylint fails to see # pylint: disable=no-member # temporarily add our dynamic methods cls = self.__class__.__module__ + '.' + self.__class__.__name__ orig_interfaces = self._dbus_class_table[cls] mock_interfaces = orig_interfaces.copy() for iface, methods in self.methods.items(): for method, impl in methods.items(): mock_interfaces.setdefault(iface, {})[method] = impl[3] self._dbus_class_table[cls] = mock_interfaces xml = dbus.service.Object.Introspect(self, object_path, connection) tree = ElementTree.fromstring(xml) for name, name_props in self.props.items(): # We might have properties for new interfaces we don't know about # yet. Try to find an existing node named after our # interface to append to, and create one if we can't. interface = tree.find(f".//interface[@name='{name}']") if interface is None: interface = ElementTree.Element("interface", {"name": name}) tree.append(interface) for prop, val in name_props.items(): if val is None: # can't guess type from None, skip continue elem = ElementTree.Element("property", { "name": prop, # We don't store the signature anywhere, so guess it. "type": dbus.lowlevel.Message.guess_signature(val), "access": "readwrite"}) interface.append(elem) xml = ElementTree.tostring(tree, encoding='utf8', method='xml').decode('utf8') # restore original class table self._dbus_class_table[cls] = orig_interfaces return xml # Overwrite dbus-python's _method_lookup(), as that offers no way to have the # same method name on different interfaces orig_method_lookup = dbus.service._method_lookup # pylint: disable=protected-access def _dbusmock_method_lookup(obj, method_name, dbus_interface): try: m = obj.methods[dbus_interface or obj.interface][method_name] return (m[3], m[3]) except KeyError: return orig_method_lookup(obj, method_name, dbus_interface) dbus.service._method_lookup = _dbusmock_method_lookup # pylint: disable=protected-access # # Helper API for templates # def get_objects() -> KeysView[str]: '''Return all existing object paths''' return objects.keys() def get_object(path) -> DBusMockObject: '''Return object for a given object path''' return objects[path] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649137868.5371122 python-dbusmock-0.27.5/dbusmock/templates/0000755000175100001710000000000000000000000021340 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/__init__.py0000644000175100001710000000057600000000000023461 0ustar00runnerdocker00000000000000'''Mock templates for common D-Bus services''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/accounts_service.py0000644000175100001710000003104500000000000025254 0ustar00runnerdocker00000000000000'''Accounts Service D-Bus mock template''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Marco Trevisan' __email__ = 'marco.trevisan@canonical.com' __copyright__ = '(c) 2021 Canonical Ltd.' __license__ = 'LGPL 3+' import sys import time import dbus from dbusmock import MOCK_IFACE, mockobject BUS_NAME = 'org.freedesktop.Accounts' MAIN_OBJ = '/org/freedesktop/Accounts' MAIN_IFACE = 'org.freedesktop.Accounts' USER_IFACE = MAIN_IFACE + '.User' SYSTEM_BUS = True DEFAULT_USER_PASSWORD = 'Pa$$wo0rd' def get_user_path(uid): return f'/org/freedesktop/Accounts/User{uid}' def load(mock, parameters=None): parameters = parameters if parameters else {} mock.mock_users = {} mock.cached_users = [] mock.automatic_login_users = set() mock.users_auto_uids = 2000 mock.AddProperties(MAIN_IFACE, mock.GetAll(MAIN_IFACE)) for uid, name in parameters.get('users', {}).items(): mock.AddUser(uid, name) def emit_properties_changed(mock, interface=MAIN_IFACE, properties=None): if properties is None: properties = mock.GetAll(interface) elif isinstance(properties, str): properties = [properties] if isinstance(properties, (list, set)): properties = {p: mock.Get(interface, p) for p in properties} elif not isinstance(properties, dict): raise TypeError('Unsupported properties type') mock.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', ( interface, properties, [])) @dbus.service.method(MOCK_IFACE, in_signature='xssa{sv}a{sv}', out_signature='o') def AddUser(self, uid, username, password, overrides, password_policy_overrides): '''Add user via uid and username and optionally overriding properties Returns the new object path. ''' path = get_user_path(uid) default_props = { 'Uid': dbus.UInt64(uid), 'UserName': username, 'RealName': username[0].upper() + username[1:] + ' Fake', 'AccountType': dbus.Int32(1), 'AutomaticLogin': False, 'BackgroundFile': '', 'Email': f'{username}@python-dbusmock.org', 'FormatsLocale': 'C', 'HomeDirectory': f'/nonexisting/mock-home/{username}', 'IconFile': '', 'InputSources': dbus.Array([], signature='a{ss}'), 'Language': 'C', 'LocalAccount': True, 'Location': '', 'Locked': False, 'LoginFrequency': dbus.UInt64(0), 'LoginHistory': dbus.Array([], signature='(xxa{sv})'), 'LoginTime': dbus.Int64(0), 'PasswordHint': 'Remember it, come on!', 'PasswordMode': 0, 'Session': 'mock-session', 'SessionType': 'wayland', 'Shell': '/usr/bin/zsh', 'SystemAccount': False, 'XHasMessages': False, 'XKeyboardLayouts': dbus.Array([], signature='s'), 'XSession': 'mock-xsession', } default_props.update(overrides if overrides else {}) self.AddObject(path, USER_IFACE, default_props, []) had_users = len(self.mock_users) != 0 had_multiple_users = len(self.mock_users) > 1 user = mockobject.objects[path] user.password = password user.properties = default_props user.pwd_expiration_policy = { 'expiration_time': sys.maxsize, 'last_change_time': int(time.time()), 'min_days_between_changes': 0, 'max_days_between_changes': 0, 'days_to_warn': 0, 'days_after_expiration_until_lock': 0, } user.pwd_expiration_policy.update( password_policy_overrides if password_policy_overrides else {}) self.mock_users[uid] = default_props self.EmitSignal(MAIN_IFACE, 'UserAdded', 'o', [path]) if not had_users: emit_properties_changed(self, MAIN_IFACE, 'HasNoUsers') elif not had_multiple_users and len(self.mock_users) > 1: emit_properties_changed(self, MAIN_IFACE, 'HasMultipleUsers') return path @dbus.service.method(MOCK_IFACE, in_signature='', out_signature='ao') def ListMockUsers(self): """ List the mock users that have been created """ return [get_user_path(uid) for uid in self.mock_users.keys()] @dbus.service.method(MOCK_IFACE, in_signature='s', out_signature='o') def AddAutoLoginUser(self, username): """ Enable autologin for an user """ path = self.FindUserByName(username) self.automatic_login_users.add(path) user = mockobject.objects[path] set_user_property(user, 'AutomaticLogin', True) emit_properties_changed(self, MAIN_IFACE, 'AutomaticLoginUsers') return path @dbus.service.method(MOCK_IFACE, in_signature='s', out_signature='o') def RemoveAutoLoginUser(self, username): """ Disables autologin for an user """ path = self.FindUserByName(username) self.automatic_login_users.remove(path) user = mockobject.objects[path] set_user_property(user, 'AutomaticLogin', False) emit_properties_changed(self, MAIN_IFACE, 'AutomaticLoginUsers') return path @dbus.service.method(MAIN_IFACE, in_signature='ssi', out_signature='o') def CreateUser(self, name, fullname, account_type): """ Creates an user using the default API """ try: self.FindUserByName(name) found = True except dbus.exceptions.DBusException: found = False if found: raise dbus.exceptions.DBusException(f'User {name} already exists', name='org.freedesktop.Accounts.Error.Failed') self.users_auto_uids += 1 return self.AddUser(self.users_auto_uids, name, DEFAULT_USER_PASSWORD, { 'RealName': fullname, 'AccountType': account_type}, {}) @dbus.service.method(MAIN_IFACE, in_signature='xb') def DeleteUser(self, uid, _remove_files): """ Removes a created user """ path = self.FindUserById(uid) had_multiple_users = len(self.mock_users) > 1 self.RemoveObject(path) self.mock_users.pop(uid) self.automatic_login_users.discard(path) self.EmitSignal(MAIN_IFACE, 'UserDeleted', 'o', [path]) if len(self.mock_users) == 0: emit_properties_changed(self, MAIN_IFACE, 'HasNoUsers') elif had_multiple_users and len(self.mock_users) < 2: emit_properties_changed(self, MAIN_IFACE, 'HasMultipleUsers') @dbus.service.method(MAIN_IFACE, in_signature='s', out_signature='o') def CacheUser(self, username): """ Cache an user """ path = self.FindUserByName(username) self.cached_users.append(path) return path @dbus.service.method(MAIN_IFACE, in_signature='s') def UncacheUser(self, username): """ Removes an user from the cache """ path = self.FindUserByName(username) self.cached_users.remove(path) @dbus.service.method(MAIN_IFACE, in_signature='', out_signature='ao') def ListCachedUsers(self): """ Lists the cached users """ return self.cached_users @dbus.service.method(MAIN_IFACE, in_signature='x', out_signature='o') def FindUserById(_self, uid): """ Finds an user by its user id """ user = mockobject.objects.get(get_user_path(uid), None) if not user: raise dbus.exceptions.DBusException( 'No such user exists', name='org.freedesktop.Accounts.Error.Failed') return get_user_path(uid) @dbus.service.method(MAIN_IFACE, in_signature='s', out_signature='o') def FindUserByName(self, username): """ Finds an user form its name """ try: [user_id] = [uid for uid, props in self.mock_users.items() if props['UserName'] == username] except ValueError as e: raise dbus.exceptions.DBusException(f'No such user exists: {e}', name='org.freedesktop.Accounts.Error.Failed') return get_user_path(user_id) @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') def GetAll(self, interface): """ Implements the GetAll dbus properties interface method. This allows to override the getters using dynamic values. """ if interface == MAIN_IFACE: return { 'DaemonVersion': 'dbus-mock-0.1', 'HasNoUsers': len(self.mock_users) == 0, 'HasMultipleUsers': len(self.mock_users) > 1, 'AutomaticLoginUsers': dbus.Array(self.automatic_login_users, signature='o'), } if interface == USER_IFACE: return self.properties return dbus.Dictionary({}, signature='sv') @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') def Get(self, interface, prop): """ Implements the Get dbus properties interface method. This allows to override the getters using dynamic values, via GetAll. """ return self.GetAll(interface)[prop] def set_user_property(user, property_name, value): """ Set an user property and emits the relative signals """ if user.properties[property_name] == value: return user.properties[property_name] = value emit_properties_changed(user, USER_IFACE, property_name) user.EmitSignal(USER_IFACE, 'Changed', '', []) @dbus.service.method(USER_IFACE, in_signature='s') def SetUserName(self, user_name): set_user_property(self, 'UserName', user_name) @dbus.service.method(USER_IFACE, in_signature='s') def SetRealName(self, real_name): set_user_property(self, 'RealName', real_name) @dbus.service.method(USER_IFACE, in_signature='s') def SetEmail(self, email): set_user_property(self, 'Email', email) @dbus.service.method(USER_IFACE, in_signature='s') def SetLanguage(self, language): set_user_property(self, 'Language', language) @dbus.service.method(USER_IFACE, in_signature='s') def SetXSession(self, x_session): set_user_property(self, 'XSession', x_session) @dbus.service.method(USER_IFACE, in_signature='s') def SetSession(self, session): set_user_property(self, 'Session', session) @dbus.service.method(USER_IFACE, in_signature='s') def SetSessionType(self, session_type): set_user_property(self, 'SessionType', session_type) @dbus.service.method(USER_IFACE, in_signature='s') def SetLocation(self, location): set_user_property(self, 'Location', location) @dbus.service.method(USER_IFACE, in_signature='s') def SetHomeDirectory(self, home_directory): set_user_property(self, 'HomeDirectory', home_directory) @dbus.service.method(USER_IFACE, in_signature='s') def SetShell(self, shell): set_user_property(self, 'Shell', shell) @dbus.service.method(USER_IFACE, in_signature='s') def SetIconFile(self, icon_file): set_user_property(self, 'IconFile', icon_file) @dbus.service.method(USER_IFACE, in_signature='s') def SetLocked(self, locked): set_user_property(self, 'Locked', locked) @dbus.service.method(USER_IFACE, in_signature='i') def SetAccountType(self, account_type): set_user_property(self, 'AccountType', account_type) @dbus.service.method(USER_IFACE, in_signature='i') def SetPasswordMode(self, password_mode): set_user_property(self, 'PasswordMode', password_mode) @dbus.service.method(USER_IFACE, in_signature='s') def SetPasswordHint(self, hint): set_user_property(self, 'PasswordHint', hint) @dbus.service.method(USER_IFACE, in_signature='ss') def SetPassword(self, password, hint): self.password = password self.SetPasswordHint(hint) @dbus.service.method(USER_IFACE, in_signature='b') def SetAutomaticLogin(self, automatic_login): manager = mockobject.objects[MAIN_OBJ] if automatic_login: manager.AddAutoLoginUser(self.properties['UserName']) else: manager.RemoveAutoLoginUser(self.properties['UserName']) @dbus.service.method(MOCK_IFACE, in_signature='xxxxxxx') def SetUserPasswordExpirationPolicy(self, uid, expiration_time, last_change_time, min_days_between_changes, max_days_between_changes, days_to_warn, days_after_expiration_until_lock): user = mockobject.objects[self.FindUserById(uid)] user.pwd_expiration_policy = { 'expiration_time': expiration_time, 'last_change_time': last_change_time, 'min_days_between_changes': min_days_between_changes, 'max_days_between_changes': max_days_between_changes, 'days_to_warn': days_to_warn, 'days_after_expiration_until_lock': days_after_expiration_until_lock, } user.EmitSignal(USER_IFACE, 'Changed', '', []) @dbus.service.method(USER_IFACE, in_signature='', out_signature='xxxxxx') def GetPasswordExpirationPolicy(self): return tuple(self.pwd_expiration_policy.values()) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/bluez5-obex.py0000644000175100001710000002471500000000000024064 0ustar00runnerdocker00000000000000# -*- coding: utf-8 -*- '''obexd mock template This creates the expected methods and properties of the object manager org.bluez.obex object (/), the manager object (/org/bluez/obex), but no agents or clients. This supports BlueZ 5 only. ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Philip Withnall' __copyright__ = '(c) 2013 Collabora Ltd.' import tempfile import os import dbus from dbusmock import OBJECT_MANAGER_IFACE, mockobject BUS_NAME = 'org.bluez.obex' MAIN_OBJ = '/' SYSTEM_BUS = False IS_OBJECT_MANAGER = True OBEX_MOCK_IFACE = 'org.bluez.obex.Mock' AGENT_MANAGER_IFACE = 'org.bluez.AgentManager1' CLIENT_IFACE = 'org.bluez.obex.Client1' SESSION_IFACE = 'org.bluez.obex.Session1' PHONEBOOK_ACCESS_IFACE = 'org.bluez.obex.PhonebookAccess1' TRANSFER_IFACE = 'org.bluez.obex.Transfer1' TRANSFER_MOCK_IFACE = 'org.bluez.obex.transfer1.Mock' def load(mock, _parameters): mock.AddObject('/org/bluez/obex', AGENT_MANAGER_IFACE, {}, [ ('RegisterAgent', 'os', '', ''), ('UnregisterAgent', 'o', '', ''), ]) obex = mockobject.objects['/org/bluez/obex'] obex.AddMethods(CLIENT_IFACE, [ ('CreateSession', 'sa{sv}', 'o', CreateSession), ('RemoveSession', 'o', '', RemoveSession), ]) @dbus.service.method(CLIENT_IFACE, in_signature='sa{sv}', out_signature='o') def CreateSession(self, destination, args): '''OBEX method to create a new transfer session. The destination must be the address of the destination Bluetooth device. The given arguments must be a map from well-known keys to values, containing at least the ‘Target’ key, whose value must be ‘PBAP’ (other keys and values are accepted by the real daemon, but not by this mock daemon at the moment). If the target is missing or incorrect, an Unsupported error is returned on the bus. Returns the path of a new Session object. ''' if 'Target' not in args or args['Target'].upper() != 'PBAP': raise dbus.exceptions.DBusException( 'Non-PBAP targets are not currently supported by this python-dbusmock template.', name=OBEX_MOCK_IFACE + '.Unsupported') # Find the first unused session ID. client_path = '/org/bluez/obex/client' session_id = 0 while client_path + '/session' + str(session_id) in mockobject.objects: session_id += 1 path = client_path + '/session' + str(session_id) properties = { 'Source': dbus.String('FIXME', variant_level=1), 'Destination': dbus.String(destination, variant_level=1), 'Channel': dbus.Byte(0, variant_level=1), 'Target': dbus.String('FIXME', variant_level=1), 'Root': dbus.String('FIXME', variant_level=1), } self.AddObject(path, SESSION_IFACE, # Properties properties, # Methods [ ('GetCapabilities', '', 's', ''), # Currently a no-op ]) session = mockobject.objects[path] session.AddMethods(PHONEBOOK_ACCESS_IFACE, [ ('Select', 'ss', '', ''), # Currently a no-op # Currently a no-op ('List', 'a{sv}', 'a(ss)', 'ret = dbus.Array(signature="(ss)")'), # Currently a no-op ('ListFilterFields', '', 'as', 'ret = dbus.Array(signature="(s)")'), ('PullAll', 'sa{sv}', 'sa{sv}', PullAll), ('GetSize', '', 'q', 'ret = 0'), ]) manager = mockobject.objects['/'] manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesAdded', 'oa{sa{sv}}', [ dbus.ObjectPath(path), {SESSION_IFACE: properties}, ]) return path @dbus.service.method(CLIENT_IFACE, in_signature='o', out_signature='') def RemoveSession(self, session_path): '''OBEX method to remove an existing transfer session. This takes the path of the transfer Session object and removes it. ''' manager = mockobject.objects['/'] # Remove all the session's transfers. transfer_id = 0 while session_path + '/transfer' + str(transfer_id) in mockobject.objects: transfer_path = session_path + '/transfer' + str(transfer_id) transfer_id += 1 self.RemoveObject(transfer_path) manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesRemoved', 'oas', [ dbus.ObjectPath(transfer_path), [TRANSFER_IFACE], ]) # Remove the session itself. self.RemoveObject(session_path) manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesRemoved', 'oas', [ dbus.ObjectPath(session_path), [SESSION_IFACE, PHONEBOOK_ACCESS_IFACE], ]) @dbus.service.method(PHONEBOOK_ACCESS_IFACE, in_signature='sa{sv}', out_signature='sa{sv}') def PullAll(self, target_file, filters): '''OBEX method to start a pull transfer of a phone book. This doesn't complete the transfer; code to mock up activating and completing the transfer must be provided by the test driver, as it’s too complex and test-specific to put here. The target_file is the absolute path for a file which will have zero or more vCards, separated by new-line characters, written to it if the method is successful (and the transfer is completed). This target_file is actually emitted in a TransferCreated signal, which is a special part of the mock interface designed to be handled by the test driver, which should then populate that file and call UpdateStatus on the Transfer object. The test driver is responsible for deleting the file once the test is complete. The filters parameter is a map of filters to be applied to the results device-side before transmitting them back to the adapter. Returns a tuple containing the path for a new Transfer D-Bus object representing the transfer, and a map of the initial properties of that Transfer object. ''' # Find the first unused session ID. session_path = self.path transfer_id = 0 while session_path + '/transfer' + str(transfer_id) in mockobject.objects: transfer_id += 1 transfer_path = session_path + '/transfer' + str(transfer_id) # Create a new temporary file to transfer to. with tempfile.NamedTemporaryFile(suffix='.vcf', prefix='tmp-bluez5-obex-PullAll_', delete=False) as temp_file: filename = os.path.abspath(temp_file.name) props = { 'Status': dbus.String('queued', variant_level=1), 'Session': dbus.ObjectPath(session_path, variant_level=1), 'Name': dbus.String(target_file, variant_level=1), 'Filename': dbus.String(filename, variant_level=1), 'Transferred': dbus.UInt64(0, variant_level=1), } self.AddObject(transfer_path, TRANSFER_IFACE, # Properties props, # Methods [ ('Cancel', '', '', ''), # Currently a no-op ]) transfer = mockobject.objects[transfer_path] transfer.AddMethods(TRANSFER_MOCK_IFACE, [ ('UpdateStatus', 'b', '', UpdateStatus), ]) manager = mockobject.objects['/'] manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesAdded', 'oa{sa{sv}}', [ dbus.ObjectPath(transfer_path), {TRANSFER_IFACE: props}, ]) # Emit a behind-the-scenes signal that a new transfer has been created. manager.EmitSignal(OBEX_MOCK_IFACE, 'TransferCreated', 'sa{sv}s', [transfer_path, filters, filename]) return (transfer_path, props) @dbus.service.signal(OBEX_MOCK_IFACE, signature='sa{sv}s') def TransferCreated(_self, _path, _filters, _transfer_filename): '''Mock signal emitted when a new Transfer object is created. This is not part of the BlueZ OBEX interface; it is purely for use by test driver code. It is emitted by the PullAll method, and is intended to be used as a signal to call UpdateStatus on the newly created Transfer (potentially after a timeout). The path is of the new Transfer object, and the filters are as provided to PullAll. The transfer filename is the full path and filename of a newly created empty temporary file which the test driver should write to. The test driver should then call UpdateStatus on the Transfer object each time a write is made to the transfer file. The final call to UpdateStatus should mark the transfer as complete. The test driver is responsible for deleting the transfer file once the test is complete. FIXME: Ideally the UpdateStatus method would only be used for completion, and all intermediate updates would be found by watching the size of the transfer file. However, that means adding a dependency on an inotify package, which seems a little much. ''' @dbus.service.method(TRANSFER_MOCK_IFACE, in_signature='b', out_signature='') def UpdateStatus(self, is_complete): '''Mock method to update the transfer status. If is_complete is False, this marks the transfer is active; otherwise it marks the transfer as complete. It is an error to call this method after calling it with is_complete as True. In both cases, it updates the number of bytes transferred to be the current size of the transfer file (whose filename was emitted in the TransferCreated signal). ''' status = 'complete' if is_complete else 'active' transferred = os.path.getsize(self.props[TRANSFER_IFACE]['Filename']) self.props[TRANSFER_IFACE]['Status'] = status self.props[TRANSFER_IFACE]['Transferred'] = dbus.UInt64(transferred, variant_level=1) self.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ TRANSFER_IFACE, { 'Status': dbus.String(status, variant_level=1), 'Transferred': dbus.UInt64(transferred, variant_level=1), }, [], ]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/bluez5.py0000644000175100001710000005653700000000000023140 0ustar00runnerdocker00000000000000# -*- coding: utf-8 -*- '''bluetoothd mock template This creates the expected methods and properties of the object manager org.bluez object (/), the manager object (/org/bluez), but no adapters or devices. This supports BlueZ 5 only. ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Philip Withnall' __copyright__ = '(c) 2013 Collabora Ltd.' import os import dbus from dbusmock import OBJECT_MANAGER_IFACE, mockobject BUS_NAME = 'org.bluez' MAIN_OBJ = '/' SYSTEM_BUS = True IS_OBJECT_MANAGER = True BLUEZ_MOCK_IFACE = 'org.bluez.Mock' AGENT_MANAGER_IFACE = 'org.bluez.AgentManager1' PROFILE_MANAGER_IFACE = 'org.bluez.ProfileManager1' ADAPTER_IFACE = 'org.bluez.Adapter1' MEDIA_IFACE = 'org.bluez.Media1' NETWORK_SERVER_IFACE = 'org.bluez.Network1' DEVICE_IFACE = 'org.bluez.Device1' # The device class of some arbitrary Android phone. MOCK_PHONE_CLASS = 5898764 @dbus.service.method(AGENT_MANAGER_IFACE, in_signature='os', out_signature='') def RegisterAgent(manager, agent_path, capability): all_caps = ['DisplayOnly', 'DisplayYesNo', 'KeyboardOnly', 'NoInputNoOutput', 'KeyboardDisplay'] if agent_path in manager.agent_paths: raise dbus.exceptions.DBusException( 'Another agent is already registered ' + manager.agent_path, name='org.bluez.Error.AlreadyExists') if capability not in all_caps: raise dbus.exceptions.DBusException( 'Unsupported capability ' + capability, name='org.bluez.Error.InvalidArguments') if not manager.default_agent: manager.default_agent = agent_path manager.agent_paths += [agent_path] manager.capabilities[str(agent_path)] = capability @dbus.service.method(AGENT_MANAGER_IFACE, in_signature='o', out_signature='') def UnregisterAgent(manager, agent_path): if agent_path not in manager.agent_paths: raise dbus.exceptions.DBusException( 'Agent not registered ' + agent_path, name='org.bluez.Error.DoesNotExist') manager.agent_paths.remove(agent_path) del manager.capabilities[agent_path] if manager.default_agent == agent_path: if len(manager.agent_paths) > 0: manager.default_agent = manager.agent_paths[-1] else: manager.default_agent = None @dbus.service.method(AGENT_MANAGER_IFACE, in_signature='o', out_signature='') def RequestDefaultAgent(manager, agent_path): if agent_path not in manager.agent_paths: raise dbus.exceptions.DBusException( 'Agent not registered ' + agent_path, name='org.bluez.Error.DoesNotExist') manager.default_agent = agent_path def load(mock, _parameters): mock.AddObject('/org/bluez', AGENT_MANAGER_IFACE, {}, [ ('RegisterAgent', 'os', '', RegisterAgent), ('RequestDefaultAgent', 'o', '', RequestDefaultAgent), ('UnregisterAgent', 'o', '', UnregisterAgent), ]) bluez = mockobject.objects['/org/bluez'] bluez.AddMethods(PROFILE_MANAGER_IFACE, [ ('RegisterProfile', 'osa{sv}', '', ''), ('UnregisterProfile', 'o', '', ''), ]) bluez.agent_paths = [] bluez.capabilities = {} bluez.default_agent = None @dbus.service.method(ADAPTER_IFACE, in_signature='o', out_signature='') def RemoveDevice(adapter, path): adapter.RemoveObject(path) manager = mockobject.objects['/'] manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesRemoved', 'oas', [ dbus.ObjectPath(path), [DEVICE_IFACE], ]) @dbus.service.method(ADAPTER_IFACE, in_signature='', out_signature='') def StartDiscovery(adapter): adapter.props[ADAPTER_IFACE]['Discovering'] = True # NOTE: discovery filter support is minimal to mock # the Discoverable discovery filter if adapter.props[ADAPTER_IFACE]['DiscoveryFilter'] is not None: adapter.props[ADAPTER_IFACE]['Discoverable'] = True adapter.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ ADAPTER_IFACE, { 'Discoverable': dbus.Boolean(adapter.props[ADAPTER_IFACE]['Discoverable'], variant_level=1), 'Discovering': dbus.Boolean(adapter.props[ADAPTER_IFACE]['Discovering'], variant_level=1), }, [], ]) @dbus.service.method(ADAPTER_IFACE, in_signature='', out_signature='') def StopDiscovery(adapter): adapter.props[ADAPTER_IFACE]['Discovering'] = False # NOTE: discovery filter support is minimal to mock # the Discoverable discovery filter if adapter.props[ADAPTER_IFACE]['DiscoveryFilter'] is not None: adapter.props[ADAPTER_IFACE]['Discoverable'] = False adapter.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ ADAPTER_IFACE, { 'Discoverable': dbus.Boolean(adapter.props[ADAPTER_IFACE]['Discoverable'], variant_level=1), 'Discovering': dbus.Boolean(adapter.props[ADAPTER_IFACE]['Discovering'], variant_level=1), }, [], ]) @dbus.service.method(ADAPTER_IFACE, in_signature='a{sv}', out_signature='') def SetDiscoveryFilter(adapter, discovery_filter): adapter.props[ADAPTER_IFACE]['DiscoveryFilter'] = discovery_filter @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='ss', out_signature='s') def AddAdapter(self, device_name, system_name): '''Convenience method to add a Bluetooth adapter You have to specify a device name which must be a valid part of an object path, e. g. "hci0", and an arbitrary system name (pretty hostname). Returns the new object path. ''' path = '/org/bluez/' + device_name address_start = int(device_name[-1]) address = f"{address_start:02d}:{address_start+1:02d}:{address_start+2:02d}:" + \ f"{address_start+3:02d}:{address_start+4:02d}:{address_start+5:02d}" adapter_properties = { 'UUIDs': dbus.Array([ # Reference: # http://git.kernel.org/cgit/bluetooth/bluez.git/tree/lib/uuid.h # PNP '00001200-0000-1000-8000-00805f9b34fb', # Generic Access Profile '00001800-0000-1000-8000-00805f9b34fb', # Generic Attribute Profile '00001801-0000-1000-8000-00805f9b34fb', # Audio/Video Remote Control Profile (remote) '0000110e-0000-1000-8000-00805f9b34fb', # Audio/Video Remote Control Profile (target) '0000110c-0000-1000-8000-00805f9b34fb', ], variant_level=1), 'Discoverable': dbus.Boolean(False, variant_level=1), 'Discovering': dbus.Boolean(False, variant_level=1), 'Pairable': dbus.Boolean(True, variant_level=1), 'Powered': dbus.Boolean(True, variant_level=1), 'Address': dbus.String(address, variant_level=1), 'AddressType': dbus.String('public', variant_level=1), 'Alias': dbus.String(system_name, variant_level=1), 'Modalias': dbus.String('usb:v1D6Bp0245d050A', variant_level=1), 'Name': dbus.String(system_name, variant_level=1), # Reference: # http://bluetooth-pentest.narod.ru/software/ # bluetooth_class_of_device-service_generator.html 'Class': dbus.UInt32(268, variant_level=1), # Computer, Laptop 'DiscoverableTimeout': dbus.UInt32(180, variant_level=1), 'PairableTimeout': dbus.UInt32(0, variant_level=1), } self.AddObject(path, ADAPTER_IFACE, # Properties adapter_properties, # Methods [ ('RemoveDevice', 'o', '', RemoveDevice), ('StartDiscovery', '', '', StartDiscovery), ('StopDiscovery', '', '', StopDiscovery), ('SetDiscoveryFilter', 'a{sv}', '', SetDiscoveryFilter), ]) adapter = mockobject.objects[path] adapter.AddMethods(MEDIA_IFACE, [ ('RegisterEndpoint', 'oa{sv}', '', ''), ('UnregisterEndpoint', 'o', '', ''), ]) adapter.AddMethods(NETWORK_SERVER_IFACE, [ ('Register', 'ss', '', ''), ('Unregister', 's', '', ''), ]) manager = mockobject.objects['/'] manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesAdded', 'oa{sa{sv}}', [ dbus.ObjectPath(path), {ADAPTER_IFACE: adapter_properties}, ]) return path @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='s') def RemoveAdapter(self, device_name): '''Convenience method to remove a Bluetooth adapter ''' path = '/org/bluez/' + device_name # We could remove the devices related to the adapters here, but # when bluez crashes, the InterfacesRemoved aren't necessarily sent # devices first, so in effect, our laziness is testing an edge case # in the clients self.RemoveObject(path) manager = mockobject.objects['/'] manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesRemoved', 'oas', [ dbus.ObjectPath(path), [ADAPTER_IFACE], ]) @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='s') def RemoveAdapterWithDevices(self, device_name): '''Convenience method to remove a Bluetooth adapter and all the devices associated to it ''' adapter_path = '/org/bluez/' + device_name adapter = mockobject.objects[adapter_path] manager = mockobject.objects['/'] to_remove = [] for path in mockobject.objects: if path.startswith(adapter_path + '/'): to_remove.append(path) for path in to_remove: adapter.RemoveObject(path) manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesRemoved', 'oas', [ dbus.ObjectPath(path), [DEVICE_IFACE], ]) self.RemoveObject(adapter_path) manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesRemoved', 'oas', [ dbus.ObjectPath(adapter_path), [ADAPTER_IFACE], ]) @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='') def Pair(device): if device.paired: raise dbus.exceptions.DBusException( 'Device already paired', name='org.bluez.Error.AlreadyExists') device_address = device.props[DEVICE_IFACE]['Address'] adapter_device_name = os.path.basename(device.props[DEVICE_IFACE]['Adapter']) device.PairDevice(adapter_device_name, device_address, MOCK_PHONE_CLASS) @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='') def Connect(device): if device.connected: raise dbus.exceptions.DBusException( 'Already Connected', name='org.bluez.Error.AlreadyConnected') device.connected = True device.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ DEVICE_IFACE, { 'Connected': dbus.Boolean(device.connected, variant_level=1), }, [], ]) @dbus.service.method(DEVICE_IFACE, in_signature='', out_signature='') def Disconnect(device): if not device.connected: raise dbus.exceptions.DBusException( 'Not Connected', name='org.bluez.Error.NotConnected') device.connected = False device.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ DEVICE_IFACE, { 'Connected': dbus.Boolean(device.connected, variant_level=1), }, [], ]) @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='sss', out_signature='s') def AddDevice(self, adapter_device_name, device_address, alias): '''Convenience method to add a Bluetooth device You have to specify a device address which must be a valid Bluetooth address (e.g. 'AA:BB:CC:DD:EE:FF'). The alias is the human-readable name for the device (e.g. as set on the device itself), and the adapter device name is the device_name passed to AddAdapter. This will create a new, unpaired and unconnected device. Returns the new object path. ''' device_name = 'dev_' + device_address.replace(':', '_').upper() adapter_path = '/org/bluez/' + adapter_device_name path = adapter_path + '/' + device_name if adapter_path not in mockobject.objects: raise dbus.exceptions.DBusException( f'Adapter {adapter_device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchAdapter') properties = { 'Address': dbus.String(device_address, variant_level=1), 'AddressType': dbus.String('public', variant_level=1), 'Name': dbus.String(alias, variant_level=1), 'Icon': dbus.String('', variant_level=1), 'Class': dbus.UInt32(0, variant_level=1), 'Appearance': dbus.UInt16(0, variant_level=1), 'UUIDs': dbus.Array([], signature='s', variant_level=1), 'Paired': dbus.Boolean(False, variant_level=1), 'Connected': dbus.Boolean(False, variant_level=1), 'Trusted': dbus.Boolean(False, variant_level=1), 'Blocked': dbus.Boolean(False, variant_level=1), 'WakeAllowed': dbus.Boolean(False, variant_level=1), 'Alias': dbus.String(alias, variant_level=1), 'Adapter': dbus.ObjectPath(adapter_path, variant_level=1), 'LegacyPairing': dbus.Boolean(False, variant_level=1), 'Modalias': dbus.String('', variant_level=1), 'RSSI': dbus.Int16(-79, variant_level=1), # arbitrary 'TxPower': dbus.Int16(0, variant_level=1), 'ManufacturerData': dbus.Array([], signature='a{qv}', variant_level=1), 'ServiceData': dbus.Array([], signature='a{sv}', variant_level=1), 'ServicesResolved': dbus.Boolean(False, variant_level=1), 'AdvertisingFlags': dbus.Array([], signature='ay', variant_level=1), 'AdvertisingData': dbus.Array([], signature='a{yv}', variant_level=1), } self.AddObject(path, DEVICE_IFACE, # Properties properties, # Methods [ ('CancelPairing', '', '', ''), ('Connect', '', '', Connect), ('ConnectProfile', 's', '', ''), ('Disconnect', '', '', Disconnect), ('DisconnectProfile', 's', '', ''), ('Pair', '', '', Pair), ]) device = mockobject.objects[path] device.paired = False device.connected = False manager = mockobject.objects['/'] manager.EmitSignal(OBJECT_MANAGER_IFACE, 'InterfacesAdded', 'oa{sa{sv}}', [ dbus.ObjectPath(path), {DEVICE_IFACE: properties}, ]) return path @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='ssi', out_signature='') def PairDevice(_self, adapter_device_name, device_address, class_): '''Convenience method to mark an existing device as paired. You have to specify a device address which must be a valid Bluetooth address (e.g. 'AA:BB:CC:DD:EE:FF'). The adapter device name is the device_name passed to AddAdapter. This unblocks the device if it was blocked. If the specified adapter or device don’t exist, a NoSuchAdapter or NoSuchDevice error will be returned on the bus. Returns nothing. ''' device_name = 'dev_' + device_address.replace(':', '_').upper() adapter_path = '/org/bluez/' + adapter_device_name device_path = adapter_path + '/' + device_name if adapter_path not in mockobject.objects: raise dbus.exceptions.DBusException( f'Adapter {adapter_device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchAdapter') if device_path not in mockobject.objects: raise dbus.exceptions.DBusException(f'Device {device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchDevice') device = mockobject.objects[device_path] device.paired = True # Based off pairing with an Android phone. uuids = [ '00001105-0000-1000-8000-00805f9b34fb', '0000110a-0000-1000-8000-00805f9b34fb', '0000110c-0000-1000-8000-00805f9b34fb', '00001112-0000-1000-8000-00805f9b34fb', '00001115-0000-1000-8000-00805f9b34fb', '00001116-0000-1000-8000-00805f9b34fb', '0000111f-0000-1000-8000-00805f9b34fb', '0000112f-0000-1000-8000-00805f9b34fb', '00001200-0000-1000-8000-00805f9b34fb', ] device.props[DEVICE_IFACE]['UUIDs'] = dbus.Array(uuids, variant_level=1) device.props[DEVICE_IFACE]['Paired'] = dbus.Boolean(True, variant_level=1) device.props[DEVICE_IFACE]['LegacyPairing'] = dbus.Boolean(True, variant_level=1) device.props[DEVICE_IFACE]['Blocked'] = dbus.Boolean(False, variant_level=1) try: device.props[DEVICE_IFACE]['Modalias'] except KeyError: device.AddProperties(DEVICE_IFACE, { 'Modalias': dbus.String('bluetooth:v000Fp1200d1436', variant_level=1), 'Class': dbus.UInt32(class_, variant_level=1), 'Icon': dbus.String('phone', variant_level=1), }) device.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ DEVICE_IFACE, { 'UUIDs': dbus.Array(uuids, variant_level=1), 'Paired': dbus.Boolean(True, variant_level=1), 'LegacyPairing': dbus.Boolean(True, variant_level=1), 'Blocked': dbus.Boolean(False, variant_level=1), 'Modalias': dbus.String('bluetooth:v000Fp1200d1436', variant_level=1), 'Class': dbus.UInt32(class_, variant_level=1), 'Icon': dbus.String('phone', variant_level=1), }, [], ]) @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='ss', out_signature='') def BlockDevice(_self, adapter_device_name, device_address): '''Convenience method to mark an existing device as blocked. You have to specify a device address which must be a valid Bluetooth address (e.g. 'AA:BB:CC:DD:EE:FF'). The adapter device name is the device_name passed to AddAdapter. This disconnects the device if it was connected. If the specified adapter or device don’t exist, a NoSuchAdapter or NoSuchDevice error will be returned on the bus. Returns nothing. ''' device_name = 'dev_' + device_address.replace(':', '_').upper() adapter_path = '/org/bluez/' + adapter_device_name device_path = adapter_path + '/' + device_name if adapter_path not in mockobject.objects: raise dbus.exceptions.DBusException( f'Adapter {adapter_device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchAdapter') if device_path not in mockobject.objects: raise dbus.exceptions.DBusException(f'Device {device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchDevice') device = mockobject.objects[device_path] device.props[DEVICE_IFACE]['Blocked'] = dbus.Boolean(True, variant_level=1) device.props[DEVICE_IFACE]['Connected'] = dbus.Boolean(False, variant_level=1) device.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ DEVICE_IFACE, { 'Blocked': dbus.Boolean(True, variant_level=1), 'Connected': dbus.Boolean(False, variant_level=1), }, [], ]) @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='ss', out_signature='') def ConnectDevice(_self, adapter_device_name, device_address): '''Convenience method to mark an existing device as connected. You have to specify a device address which must be a valid Bluetooth address (e.g. 'AA:BB:CC:DD:EE:FF'). The adapter device name is the device_name passed to AddAdapter. This unblocks the device if it was blocked. If the specified adapter or device don’t exist, a NoSuchAdapter or NoSuchDevice error will be returned on the bus. Returns nothing. ''' device_name = 'dev_' + device_address.replace(':', '_').upper() adapter_path = '/org/bluez/' + adapter_device_name device_path = adapter_path + '/' + device_name if adapter_path not in mockobject.objects: raise dbus.exceptions.DBusException( f'Adapter {adapter_device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchAdapter') if device_path not in mockobject.objects: raise dbus.exceptions.DBusException( f'Device {device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchDevice') device = mockobject.objects[device_path] device.props[DEVICE_IFACE]['Blocked'] = dbus.Boolean(False, variant_level=1) device.props[DEVICE_IFACE]['Connected'] = dbus.Boolean(True, variant_level=1) device.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ DEVICE_IFACE, { 'Blocked': dbus.Boolean(False, variant_level=1), 'Connected': dbus.Boolean(True, variant_level=1), }, [], ]) @dbus.service.method(BLUEZ_MOCK_IFACE, in_signature='ss', out_signature='') def DisconnectDevice(_self, adapter_device_name, device_address): '''Convenience method to mark an existing device as disconnected. You have to specify a device address which must be a valid Bluetooth address (e.g. 'AA:BB:CC:DD:EE:FF'). The adapter device name is the device_name passed to AddAdapter. This does not change the device’s blocked status. If the specified adapter or device don’t exist, a NoSuchAdapter or NoSuchDevice error will be returned on the bus. Returns nothing. ''' device_name = 'dev_' + device_address.replace(':', '_').upper() adapter_path = '/org/bluez/' + adapter_device_name device_path = adapter_path + '/' + device_name if adapter_path not in mockobject.objects: raise dbus.exceptions.DBusException( f'Adapter {adapter_device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchAdapter') if device_path not in mockobject.objects: raise dbus.exceptions.DBusException( f'Device {device_name} does not exist.', name=BLUEZ_MOCK_IFACE + '.NoSuchDevice') device = mockobject.objects[device_path] device.props[DEVICE_IFACE]['Connected'] = dbus.Boolean(False, variant_level=1) device.EmitSignal(dbus.PROPERTIES_IFACE, 'PropertiesChanged', 'sa{sv}as', [ DEVICE_IFACE, { 'Connected': dbus.Boolean(False, variant_level=1), }, [], ]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/gnome_screensaver.py0000644000175100001710000000225300000000000025421 0ustar00runnerdocker00000000000000'''gnome-shell screensaver mock template This creates the expected methods and properties of the org.gnome.ScreenSaver object. ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Bastien Nocera' __copyright__ = '(c) 2013 Red Hat Inc.' BUS_NAME = 'org.gnome.ScreenSaver' MAIN_OBJ = '/org/gnome/ScreenSaver' MAIN_IFACE = 'org.gnome.ScreenSaver' SYSTEM_BUS = False def load(mock, _parameters): mock.AddMethods(MAIN_IFACE, [ ('GetActive', '', 'b', 'ret = self.is_active'), ('GetActiveTime', '', 'u', 'ret = 1'), ('SetActive', 'b', '', 'self.is_active = args[0]; self.EmitSignal(' '"", "ActiveChanged", "b", [self.is_active])'), ('Lock', '', '', 'time.sleep(1); self.SetActive(True)'), ('ShowMessage', 'sss', '', ''), ('SimulateUserActivity', '', '', ''), ]) # default state mock.is_active = False ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/iio-sensors-proxy.py0000644000175100001710000001517600000000000025355 0ustar00runnerdocker00000000000000'''sensors proxy mock template ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Marco Trevisan' __copyright__ = '(c) 2021 Canonical Ltd.' import re import dbus from dbusmock import MOCK_IFACE BUS_NAME = 'net.hadess.SensorProxy' MAIN_OBJ = '/net/hadess/SensorProxy' MAIN_IFACE = 'net.hadess.SensorProxy' COMPASS_IFACE = 'net.hadess.SensorProxy.Compass' SYSTEM_BUS = True CAMEL_TO_SNAKE_CASE_RE = re.compile(r'(? 0: dev = devices[0] if dev: activate_connection(NM, connection_path, dev, connection_path) return connection_path @dbus.service.method(SETTINGS_IFACE, in_signature='s', out_signature='o') def SettingsGetConnectionByUuid(self, conn_uuid): conns = self.ListConnections() for o in conns: self.conn = dbusmock.get_object(o) settings = self.conn.GetSettings() if settings['connection']['uuid'] == conn_uuid: return o raise dbus.exceptions.DBusException(f"There was no connection with uuid {conn_uuid}") def ConnectionUpdate(self, settings): '''Update settings on a connection. settings is a String String Variant Map Map. See https://developer.gnome.org/NetworkManager/0.9/spec.html#type-String_String_Variant_Map_Map ''' connection_path = self.connection_path NM = dbusmock.get_object(MANAGER_OBJ) settings_obj = dbusmock.get_object(SETTINGS_OBJ) main_connections = settings_obj.ListConnections() if connection_path not in main_connections: raise dbus.exceptions.DBusException( f'Connection {connection_path} does not exist', name=MANAGER_IFACE + '.DoesNotExist',) # Take care not to overwrite the secrets for setting_name in settings: setting = settings[setting_name] for k in setting: if setting_name not in self.settings: self.settings[setting_name] = {} self.settings[setting_name][k] = setting[k] self.EmitSignal(CSETTINGS_IFACE, 'Updated', '', []) auto_connect = False if 'autoconnect' in settings['connection']: auto_connect = settings['connection']['autoconnect'] if auto_connect: dev = None devices = NM.GetDevices() # Grab the first device. if len(devices) > 0: dev = devices[0] if dev: activate_connection(NM, connection_path, dev, connection_path) return connection_path def ConnectionGetSettings(self): # Deep copy the settings with the secrets stripped # out. (NOTE: copy.deepcopy doesn't work with dbus # types). settings = {} for setting_name in self.settings: setting = self.settings[setting_name] for k in setting: if k != 'secrets': if setting_name not in settings: settings[setting_name] = {} settings[setting_name][k] = setting[k] return settings def ConnectionGetSecrets(self, setting): settings = self.settings[setting] if 'secrets' in settings: secrets = {setting: {'secrets': settings['secrets']}} else: secrets = {setting: {'secrets': {'no-secrets': True}}} return secrets def ConnectionDelete(self): '''Deletes a connection. This also * removes the deleted connection from any device, * removes any active connection(s) it might be associated with, * removes it from the Settings interface, * as well as deletes the object from the mock. Note: If this was the only active connection, we change the global connection state. ''' connection_path = self.connection_path NM = dbusmock.get_object(MANAGER_OBJ) settings_obj = dbusmock.get_object(SETTINGS_OBJ) # Find the associated active connection(s). active_connections = NM.Get(MANAGER_IFACE, 'ActiveConnections') associated_active_connections = [] for ac in active_connections: ac_obj = dbusmock.get_object(ac) ac_con = ac_obj.Get(ACTIVE_CONNECTION_IFACE, 'Connection') if ac_con == connection_path: associated_active_connections.append(ac) # We found that the connection we are deleting are associated to all # active connections and subsequently set the global state to # disconnected. if len(active_connections) == len(associated_active_connections): self.SetGlobalConnectionState(NMState.NM_STATE_DISCONNECTED) # Remove the connection from all associated devices. # We also remove all associated active connections. for dev_path in NM.GetDevices(): dev_obj = dbusmock.get_object(dev_path) connections = dev_obj.Get(DEVICE_IFACE, 'AvailableConnections') for ac in associated_active_connections: NM.RemoveActiveConnection(dev_path, ac) if connection_path not in connections: continue connections.remove(dbus.ObjectPath(connection_path)) dev_obj.Set(DEVICE_IFACE, 'AvailableConnections', connections) # Remove the connection from the settings interface main_connections = settings_obj.ListConnections() if connection_path not in main_connections: return main_connections.remove(connection_path) settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) settings_obj.EmitSignal(SETTINGS_IFACE, 'ConnectionRemoved', 'o', [connection_path]) # Remove the connection from the mock connection_obj = dbusmock.get_object(connection_path) connection_obj.EmitSignal(CSETTINGS_IFACE, 'Removed', '', []) self.object_manager_emit_removed(connection_path) self.RemoveObject(connection_path) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/notification_daemon.py0000644000175100001710000000326200000000000025726 0ustar00runnerdocker00000000000000'''notification-daemon mock template This creates the expected methods and properties of the notification-daemon services, but no devices. You can specify non-default capabilities in "parameters". ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' BUS_NAME = 'org.freedesktop.Notifications' MAIN_OBJ = '/org/freedesktop/Notifications' MAIN_IFACE = 'org.freedesktop.Notifications' SYSTEM_BUS = False # default capabilities, can be modified with "capabilities" parameter default_caps = ['body', 'body-markup', 'icon-static', 'image/svg+xml', 'private-synchronous', 'append', 'private-icon-only', 'truncation'] def load(mock, parameters): if 'capabilities' in parameters: caps = parameters['capabilities'].split() else: caps = default_caps # next notification ID mock.next_id = 1 mock.AddMethods(MAIN_IFACE, [ ('GetCapabilities', '', 'as', f'ret = {repr(caps)}'), ('CloseNotification', 'i', '', 'if args[0] < self.next_id: self.EmitSignal(' '"", "NotificationClosed", "uu", [args[0], 1])'), ('GetServerInformation', '', 'ssss', 'ret = ("mock-notify", "test vendor", "1.0", "1.1")'), ('Notify', 'susssasa{sv}i', 'u', '''if args[1]: ret = args[1] else: ret = self.next_id self.next_id += 1 '''), ]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/ofono.py0000644000175100001710000004320700000000000023040 0ustar00runnerdocker00000000000000'''ofonod D-Bus mock template''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2013 Canonical Ltd.' import dbus import dbusmock BUS_NAME = 'org.ofono' MAIN_OBJ = '/' MAIN_IFACE = 'org.ofono.Manager' SYSTEM_BUS = True NOT_IMPLEMENTED = 'raise dbus.exceptions.DBusException("not implemented", name="org.ofono.Error.NotImplemented")' # interface org.ofono.Manager { # methods: # GetModems(out a(oa{sv}) modems); # signals: # ModemAdded(o path, # a{sv} properties); # ModemRemoved(o path); # }; _parameters = {} def load(mock, parameters): global _parameters # pylint: disable=global-statement mock.modems = [] # object paths mock.modem_serial_counter = 0 mock.imsi_counter = 0 mock.iccid_counter = 0 _parameters = parameters mock.AddMethod(MAIN_IFACE, 'GetModems', '', 'a(oa{sv})', 'ret = [(m, objects[m].GetAll("org.ofono.Modem")) for m in self.modems]') if not parameters.get('no_modem', False): mock.AddModem(parameters.get('ModemName', 'ril_0'), {}) # interface org.ofono.Modem { # methods: # GetProperties(out a{sv} properties); # SetProperty(in s property, # in v value); # signals: # PropertyChanged(s name, # v value); # }; @dbus.service.method(dbusmock.MOCK_IFACE, in_signature='sa{sv}', out_signature='s') def AddModem(self, name, _properties): '''Convenience method to add a modem You have to specify a device name which must be a valid part of an object path, e. g. "mock_ac". For future extensions you can specify a "properties" array, but no extra properties are supported for now. Returns the new object path. ''' path = '/' + name self.AddObject(path, 'org.ofono.Modem', { 'Online': dbus.Boolean(True, variant_level=1), 'Powered': dbus.Boolean(True, variant_level=1), 'Lockdown': dbus.Boolean(False, variant_level=1), 'Emergency': dbus.Boolean(False, variant_level=1), 'Manufacturer': dbus.String('Fakesys', variant_level=1), 'Model': dbus.String('Mock Modem', variant_level=1), 'Revision': dbus.String('0815.42', variant_level=1), 'Serial': dbus.String(new_modem_serial(self), variant_level=1), 'Type': dbus.String('hardware', variant_level=1), 'Interfaces': ['org.ofono.CallVolume', 'org.ofono.VoiceCallManager', 'org.ofono.NetworkRegistration', 'org.ofono.SimManager', # 'org.ofono.MessageManager', 'org.ofono.ConnectionManager', # 'org.ofono.NetworkTime' ], # 'Features': ['sms', 'net', 'gprs', 'sim'] 'Features': ['gprs', 'net'], }, [ ('GetProperties', '', 'a{sv}', 'ret = self.GetAll("org.ofono.Modem")'), ('SetProperty', 'sv', '', 'self.Set("org.ofono.Modem", args[0], args[1]); ' 'self.EmitSignal("org.ofono.Modem", "PropertyChanged",' ' "sv", [args[0], args[1]])'), ] ) obj = dbusmock.mockobject.objects[path] obj.name = name add_voice_call_api(obj) add_netreg_api(obj) add_simmanager_api(self, obj) add_connectionmanager_api(obj) self.modems.append(path) props = obj.GetAll('org.ofono.Modem', dbus_interface=dbus.PROPERTIES_IFACE) self.EmitSignal(MAIN_IFACE, 'ModemAdded', 'oa{sv}', [path, props]) return path # Generate a new modem serial number so each modem we add gets a unique one. # Use a counter so that the result is predictable for tests. def new_modem_serial(mock): serial = f'12345678-1234-1234-1234-{mock.modem_serial_counter:012d}' mock.modem_serial_counter += 1 return serial # Generate a new unique IMSI (start with USA/AT&T 310/150 to match the MCC/MNC SIM properties) # Use a counter so that the result is predictable for tests. def new_imsi(mock): imsi = f'310150{mock.imsi_counter:09d}' mock.imsi_counter += 1 return imsi # Generate a new unique ICCID # Use a counter so that the result is predictable for tests. def new_iccid(mock): iccid = f'893581234{mock.iccid_counter:012d}' mock.iccid_counter += 1 return iccid # interface org.ofono.VoiceCallManager { # methods: # GetProperties(out a{sv} properties); # Dial(in s number, # in s hide_callerid, # out o path); # Transfer(); # SwapCalls(); # ReleaseAndAnswer(); # ReleaseAndSwap(); # HoldAndAnswer(); # HangupAll(); # PrivateChat(in o call, # out ao calls); # CreateMultiparty(out o calls); # HangupMultiparty(); # SendTones(in s SendTones); # GetCalls(out a(oa{sv}) calls_with_properties); # signals: # Forwarded(s type); # BarringActive(s type); # PropertyChanged(s name, # v value); # CallAdded(o path, # a{sv} properties); # CallRemoved(o path); # }; def add_voice_call_api(mock): '''Add org.ofono.VoiceCallManager API to a mock''' # also add an emergency number which is not a real one, in case one runs a # test case against a production ofono :-) mock.AddProperty('org.ofono.VoiceCallManager', 'EmergencyNumbers', ['911', '13373']) mock.calls = [] # object paths mock.AddMethods('org.ofono.VoiceCallManager', [ ('GetProperties', '', 'a{sv}', 'ret = self.GetAll("org.ofono.VoiceCallManager")'), ('Transfer', '', '', ''), ('SwapCalls', '', '', ''), ('ReleaseAndAnswer', '', '', ''), ('ReleaseAndSwap', '', '', ''), ('HoldAndAnswer', '', '', ''), ('SendTones', 's', '', ''), ('PrivateChat', 'o', 'ao', NOT_IMPLEMENTED), ('CreateMultiparty', '', 'o', NOT_IMPLEMENTED), ('HangupMultiparty', '', '', NOT_IMPLEMENTED), ('GetCalls', '', 'a(oa{sv})', 'ret = [(c, objects[c].GetAll("org.ofono.VoiceCall")) for c in self.calls]') ]) @dbus.service.method('org.ofono.VoiceCallManager', in_signature='ss', out_signature='s') def Dial(self, number, _hide_callerid): # pylint: disable=protected-access # _object_path path = f'{self._object_path}/voicecall{(len(self.calls) + 1):02}' self.AddObject(path, 'org.ofono.VoiceCall', { 'State': dbus.String('dialing', variant_level=1), 'LineIdentification': dbus.String(number, variant_level=1), 'Name': dbus.String('', variant_level=1), 'Multiparty': dbus.Boolean(False, variant_level=1), 'RemoteHeld': dbus.Boolean(False, variant_level=1), 'RemoteMultiparty': dbus.Boolean(False, variant_level=1), 'Emergency': dbus.Boolean(False, variant_level=1), }, [ ('GetProperties', '', 'a{sv}', 'ret = self.GetAll("org.ofono.VoiceCall")'), ('Deflect', 's', '', NOT_IMPLEMENTED), ('Hangup', '', '', 'self.parent.calls.remove(self._object_path);' 'self.parent.RemoveObject(self._object_path);' 'self.EmitSignal("org.ofono.VoiceCallManager", "CallRemoved", "o", [self._object_path])'), ('Answer', '', '', NOT_IMPLEMENTED), ] ) obj = dbusmock.mockobject.objects[path] obj.parent = self self.calls.append(path) self.EmitSignal('org.ofono.VoiceCallManager', 'CallAdded', 'oa{sv}', [path, obj.GetProperties()]) return path @dbus.service.method('org.ofono.VoiceCallManager', in_signature='', out_signature='') def HangupAll(self): print('XXX HangupAll', self.calls) for c in list(self.calls): # needs a copy dbusmock.mockobject.objects[c].Hangup() assert self.calls == [] # interface org.ofono.NetworkRegistration { # methods: # GetProperties(out a{sv} properties); # SetProperty(in s property, # in v value); # Register(); # GetOperators(out a(oa{sv}) operators_with_properties); # Scan(out a(oa{sv}) operators_with_properties); # signals: # PropertyChanged(s name, # v value); # }; # # for //operator/: # interface org.ofono.NetworkOperator { # methods: # GetProperties(out a{sv} properties); # Register(); # signals: # PropertyChanged(s name, # v value); # properties: # }; def get_all_operators(mock): return 'ret = [(m, objects[m].GetAll("org.ofono.NetworkOperator")) ' \ f'for m in objects if "{mock.name}/operator/" in m]' def add_netreg_api(mock): '''Add org.ofono.NetworkRegistration API to a mock''' # also add an emergency number which is not a real one, in case one runs a # test case against a production ofono :-) mock.AddProperties('org.ofono.NetworkRegistration', { 'Mode': 'auto', 'Status': 'registered', 'LocationAreaCode': _parameters.get('LocationAreaCode', 987), 'CellId': _parameters.get('CellId', 10203), 'MobileCountryCode': _parameters.get('MobileCountryCode', '777'), 'MobileNetworkCode': _parameters.get('MobileNetworkCode', '11'), 'Technology': _parameters.get('Technology', 'gsm'), 'Name': _parameters.get('Name', 'fake.tel'), 'Strength': _parameters.get('Strength', dbus.Byte(80)), 'BaseStation': _parameters.get('BaseStation', ''), }) mock.AddObject(f'/{mock.name}/operator/op1', 'org.ofono.NetworkOperator', { 'Name': _parameters.get('Name', 'fake.tel'), 'Status': 'current', 'MobileCountryCode': _parameters.get('MobileCountryCode', '777'), 'MobileNetworkCode': _parameters.get('MobileNetworkCode', '11'), 'Technologies': [_parameters.get('Technology', 'gsm')], }, [ ('GetProperties', '', 'a{sv}', 'ret = self.GetAll("org.ofono.NetworkOperator")'), ('Register', '', '', ''), ] # noqa: silly pep8 error here about hanging indent ) mock.AddMethods('org.ofono.NetworkRegistration', [ ('GetProperties', '', 'a{sv}', 'ret = self.GetAll("org.ofono.NetworkRegistration")'), ('SetProperty', 'sv', '', 'self.Set("org.ofono.NetworkRegistration", args[0], args[1]); ' 'self.EmitSignal("org.ofono.NetworkRegistration", "PropertyChanged", "sv", [args[0], args[1]])'), ('Register', '', '', ''), ('GetOperators', '', 'a(oa{sv})', get_all_operators(mock)), ('Scan', '', 'a(oa{sv})', get_all_operators(mock)), ]) # interface org.ofono.SimManager { # methods: # GetProperties(out a{sv} properties); # SetProperty(in s property, # in v value); # ChangePin(in s type, # in s oldpin, # in s newpin); # EnterPin(in s type, # in s pin); # ResetPin(in s type, # in s puk, # in s newpin); # LockPin(in s type, # in s pin); # UnlockPin(in s type, # in s pin); # GetIcon(in y id, # out ay icon); # signals: # PropertyChanged(s name, # v value); # }; def add_simmanager_api(self, mock): '''Add org.ofono.SimManager API to a mock''' iface = 'org.ofono.SimManager' mock.AddProperties(iface, { 'BarredDialing': _parameters.get('BarredDialing', False), 'CardIdentifier': _parameters.get('CardIdentifier', new_iccid(self)), 'FixedDialing': _parameters.get('FixedDialing', False), 'LockedPins': _parameters.get('LockedPins', dbus.Array([], signature='s')), 'MobileCountryCode': _parameters.get('MobileCountryCode', '310'), 'MobileNetworkCode': _parameters.get('MobileNetworkCode', '150'), 'PreferredLanguages': _parameters.get('PreferredLanguages', ['en']), 'Present': _parameters.get('Present', dbus.Boolean(True)), 'Retries': _parameters.get('Retries', dbus.Dictionary([["pin", dbus.Byte(3)], ["puk", dbus.Byte(10)]])), 'PinRequired': _parameters.get('PinRequired', "none"), 'SubscriberNumbers': _parameters.get('SubscriberNumbers', ['123456789', '234567890']), 'SubscriberIdentity': _parameters.get('SubscriberIdentity', new_imsi(self)), }) mock.AddMethods(iface, [ ('GetProperties', '', 'a{sv}', f'ret = self.GetAll("{iface}")'), ('SetProperty', 'sv', '', f'self.Set("{iface}", args[0], args[1]); ' 'self.EmitSignal("{iface}", "PropertyChanged", "sv", [args[0], args[1]])'), ('ChangePin', 'sss', '', ''), ('EnterPin', 'ss', '', 'correctPin = "1234"\n' f'iface = "{iface}"\n' 'newRetries = self.Get(iface, "Retries")\n' 'if args[0] == "pin" and args[1] != correctPin:\n' ' newRetries["pin"] = dbus.Byte(newRetries["pin"] - 1)\n' 'elif args[0] == "pin":\n' ' newRetries["pin"] = dbus.Byte(3)\n' 'self.Set(iface, "Retries", newRetries)\n' 'self.EmitSignal(iface, "PropertyChanged", "sv", ["Retries", newRetries])\n' 'if args[0] == "pin" and args[1] != correctPin:\n' ' class Failed(dbus.exceptions.DBusException):\n' ' _dbus_error_name = "org.ofono.Error.Failed"\n' ' raise Failed("Operation failed")'), ('ResetPin', 'sss', '', 'correctPuk = "12345678"\n' f'iface = "{iface}"\n' 'newRetries = self.Get(iface, "Retries")\n' 'if args[0] == "puk" and args[1] != correctPuk:\n' ' newRetries["puk"] = dbus.Byte(newRetries["puk"] - 1)\n' 'elif args[0] == "puk":\n' ' newRetries["pin"] = dbus.Byte(3)\n' ' newRetries["puk"] = dbus.Byte(10)\n' 'self.Set(iface, "Retries", newRetries)\n' 'self.EmitSignal(iface, "PropertyChanged", "sv", ["Retries", newRetries])\n' 'if args[0] == "puk" and args[1] != correctPuk:\n' ' class Failed(dbus.exceptions.DBusException):\n' ' _dbus_error_name = "org.ofono.Error.Failed"\n' ' raise Failed("Operation failed")'), ('LockPin', 'ss', '', ''), ('UnlockPin', 'ss', '', ''), ]) # interface org.ofono.ConnectionManager { # methods: # GetProperties(out a{sv} properties); # SetProperty(in s property, # in v value); # AddContext(in s type, # out o path); # RemoveContext(in o path); # DeactivateAll(); # GetContexts(out a(oa{sv}) contexts_with_properties); # signals: # PropertyChanged(s name, # v value); # ContextAdded(o path, # v properties); # ContextRemoved(o path); # }; def add_connectionmanager_api(mock): '''Add org.ofono.ConnectionManager API to a mock''' iface = 'org.ofono.ConnectionManager' mock.AddProperties(iface, { 'Attached': _parameters.get('Attached', True), 'Bearer': _parameters.get('Bearer', 'gprs'), 'RoamingAllowed': _parameters.get('RoamingAllowed', False), 'Powered': _parameters.get('ConnectionPowered', True), }) mock.AddMethods(iface, [ ('GetProperties', '', 'a{sv}', f'ret = self.GetAll("{iface}")'), ('SetProperty', 'sv', '', f'self.Set("{iface}", args[0], args[1]); ' 'self.EmitSignal("{iface}", "PropertyChanged", "sv", [args[0], args[1]])'), ('AddContext', 's', 'o', 'ret = "/"'), ('RemoveContext', 'o', '', ''), ('DeactivateAll', '', '', ''), ('GetContexts', '', 'a(oa{sv})', 'ret = dbus.Array([])'), ]) # unimplemented Modem object interfaces: # # interface org.ofono.NetworkTime { # methods: # GetNetworkTime(out a{sv} time); # signals: # NetworkTimeChanged(a{sv} time); # properties: # }; # interface org.ofono.MessageManager { # methods: # GetProperties(out a{sv} properties); # SetProperty(in s property, # in v value); # SendMessage(in s to, # in s text, # out o path); # GetMessages(out a(oa{sv}) messages); # signals: # PropertyChanged(s name, # v value); # IncomingMessage(s message, # a{sv} info); # ImmediateMessage(s message, # a{sv} info); # MessageAdded(o path, # a{sv} properties); # MessageRemoved(o path); # }; # interface org.ofono.CallVolume { # methods: # GetProperties(out a{sv} properties); # SetProperty(in s property, # in v value); # signals: # PropertyChanged(s property, # v value); # }; ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/polkitd.py0000644000175100001710000000706100000000000023364 0ustar00runnerdocker00000000000000'''polkitd mock template This creates the expected methods and properties of the main org.freedesktop.PolicyKit1 object. By default, all actions are rejected. You can call AllowUnknown() and SetAllowed() on the mock D-Bus interface to control which actions are allowed. ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2013-2021 Canonical Ltd.' import time import dbus from dbusmock import MOCK_IFACE BUS_NAME = 'org.freedesktop.PolicyKit1' MAIN_OBJ = '/org/freedesktop/PolicyKit1/Authority' MAIN_IFACE = 'org.freedesktop.PolicyKit1.Authority' SYSTEM_BUS = True def load(mock, _parameters): mock.AddProperties(MAIN_IFACE, dbus.Dictionary({ 'BackendName': 'local', 'BackendVersion': '0.8.15', 'BackendFeatures': dbus.UInt32(1, variant_level=1), }, signature='sv')) # default state mock.allow_unknown = False mock.allowed = [] mock.delay = 0 mock.simulate_hang = False mock.hanging_actions = [] mock.hanging_calls = [] @dbus.service.method(MAIN_IFACE, in_signature='(sa{sv})sa{ss}us', out_signature='(bba{ss})', async_callbacks=('ok_cb', '_err_cb')) def CheckAuthorization(self, _subject, action_id, _details, _flags, _cancellation_id, ok_cb, _err_cb): time.sleep(self.delay) allowed = action_id in self.allowed or self.allow_unknown ret = (allowed, False, {'test': 'test'}) if self.simulate_hang or action_id in self.hanging_actions: self.hanging_calls.append((ok_cb, ret)) else: ok_cb(ret) @dbus.service.method(MAIN_IFACE, in_signature='(sa{sv})ss') def RegisterAuthenticationAgent(_self, _subject, _locale, _object_path): pass @dbus.service.method(MOCK_IFACE, in_signature='b', out_signature='') def AllowUnknown(self, default): '''Control whether unknown actions are allowed This controls the return value of CheckAuthorization for actions which were not explicitly allowed by SetAllowed(). ''' self.allow_unknown = default @dbus.service.method(MOCK_IFACE, in_signature='as', out_signature='') def SetAllowed(self, actions): '''Set allowed actions''' self.allowed = actions @dbus.service.method(MOCK_IFACE, in_signature='d', out_signature='') def SetDelay(self, delay): '''Makes the CheckAuthorization() method to delay''' self.delay = delay @dbus.service.method(MOCK_IFACE, in_signature='b', out_signature='') def SimulateHang(self, hang): '''Makes the CheckAuthorization() method to hang''' self.simulate_hang = hang @dbus.service.method(MOCK_IFACE, in_signature='as', out_signature='') def SimulateHangActions(self, actions): '''Makes the CheckAuthorization() method to hang on such actions''' self.hanging_actions = actions @dbus.service.method(MOCK_IFACE, in_signature='', out_signature='') def ReleaseHangingCalls(self): '''Calls all the hanging callbacks''' for (cb, ret) in self.hanging_calls: cb(ret) self.hanging_calls = [] @dbus.service.method(MOCK_IFACE, in_signature='', out_signature='b') def HaveHangingCalls(self): '''Check if we've hangling calls''' return len(self.hanging_calls) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/power_profiles_daemon.py0000644000175100001710000000514000000000000026274 0ustar00runnerdocker00000000000000'''power-profiles-daemon mock template This creates the expected methods and properties of the main net.hadess.PowerProfiles object. This provides only the non-deprecated D-Bus API as of version 0.9. ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Bastien Nocera' __copyright__ = '(c) 2021, Red Hat Inc.' import dbus BUS_NAME = 'net.hadess.PowerProfiles' MAIN_OBJ = '/net/hadess/PowerProfiles' MAIN_IFACE = 'net.hadess.PowerProfiles' SYSTEM_BUS = True def hold_profile(self, profile, reason, application_id): self.cookie += 1 element = {'Profile': profile, 'Reason': reason, 'ApplicationId': application_id} self.holds[self.cookie] = element self.props[MAIN_IFACE]['ActiveProfileHolds'] = [] for value in self.holds.values(): self.props[MAIN_IFACE]['ActiveProfileHolds'].append(value) return self.cookie def release_profile(self, cookie): self.holds.pop(cookie) self.props[MAIN_IFACE]['ActiveProfileHolds'] = [] for value in self.holds.values(): self.props[MAIN_IFACE]['ActiveProfileHolds'].append(value) if len(self.props[MAIN_IFACE]['ActiveProfileHolds']) == 0: self.props[MAIN_IFACE]['ActiveProfileHolds'] = \ dbus.Array([], signature='(aa{sv})') def load(mock, parameters): # Loaded! mock.loaded = True mock.cookie = 0 mock.hold_profile = hold_profile mock.release_profile = release_profile mock.holds = {} props = { 'ActiveProfile': parameters.get('ActiveProfile', 'balanced'), 'PerformanceDegraded': parameters.get('PerformanceDegraded', ''), 'Profiles': [ dbus.Dictionary({'Profile': 'power-saver', 'Driver': 'dbusmock'}, signature='sv'), dbus.Dictionary({'Profile': 'balanced', 'Driver': 'dbusmock'}, signature='sv'), dbus.Dictionary({'Profile': 'performance', 'Driver': 'dbusmock'}, signature='sv') ], 'Actions': dbus.Array([], signature='(as)'), 'ActiveProfileHolds': dbus.Array([], signature='(aa{sv})'), } mock.AddProperties(MAIN_IFACE, dbus.Dictionary(props, signature='sv')) mock.AddMethods(MAIN_IFACE, [ ('HoldProfile', 'sss', 'u', 'ret = self.hold_profile(self, args[0], args[1], args[2])'), ('ReleaseProfile', 'u', '', 'self.release_profile(self, args[0])'), ]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/systemd.py0000644000175100001710000000604200000000000023404 0ustar00runnerdocker00000000000000'''systemd mock template ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Jonas Ådahl' __copyright__ = '(c) 2021 Red Hat' from gi.repository import GLib import dbus from dbusmock import MOCK_IFACE, mockobject BUS_PREFIX = 'org.freedesktop.systemd1' PATH_PREFIX = '/org/freedesktop/systemd1' BUS_NAME = BUS_PREFIX MAIN_OBJ = PATH_PREFIX MAIN_IFACE = BUS_PREFIX + '.Manager' UNIT_IFACE = BUS_PREFIX + '.Unit' SYSTEM_BUS = True def load(mock, _parameters): mock.next_job_id = 1 mock.units = {} mock.AddProperties(MAIN_IFACE, {'Version': 'v246'}) def escape_unit_name(name): for s in ['.', '-']: name = name.replace(s, '_') return name def emit_job_new_remove(mock, job_id, job_path, name): mock.EmitSignal(MAIN_IFACE, 'JobNew', 'uos', [job_id, job_path, name]) mock.EmitSignal(MAIN_IFACE, 'JobRemoved', 'uoss', [job_id, job_path, name, 'done']) @dbus.service.method(MAIN_IFACE, in_signature='ss', out_signature='o') def StartUnit(self, name, _mode): job_id = self.next_job_id self.next_job_id += 1 job_path = f'{PATH_PREFIX}/Job/{job_id}' GLib.idle_add(lambda: emit_job_new_remove(self, job_id, job_path, name)) unit_path = self.units[str(name)] unit = mockobject.objects[unit_path] unit.UpdateProperties(UNIT_IFACE, {'ActiveState': 'active'}) return job_path @dbus.service.method(MAIN_IFACE, in_signature='ssa(sv)a(sa(sv))', out_signature='o') def StartTransientUnit(self, name, _mode, _properties, _aux): job_id = self.next_job_id self.next_job_id += 1 job_path = f'{PATH_PREFIX}/Job/{job_id}' GLib.idle_add(lambda: emit_job_new_remove(self, job_id, job_path, name)) return job_path @dbus.service.method(MAIN_IFACE, in_signature='ss', out_signature='o') def StopUnit(self, name, _mode): job_id = self.next_job_id self.next_job_id += 1 job_path = f'{PATH_PREFIX}/Job/{job_id}' GLib.idle_add(lambda: emit_job_new_remove(self, job_id, job_path, name)) unit_path = self.units[str(name)] unit = mockobject.objects[unit_path] unit.UpdateProperties(UNIT_IFACE, {'ActiveState': 'inactive'}) return job_path @dbus.service.method(MAIN_IFACE, in_signature='s', out_signature='o') def GetUnit(self, name): unit_path = self.units[str(name)] return unit_path @dbus.service.method(MOCK_IFACE, in_signature='s') def AddMockUnit(self, name): unit_path = f'{PATH_PREFIX}/unit/{escape_unit_name(name)}' self.units[str(name)] = unit_path self.AddObject(unit_path, UNIT_IFACE, { 'Id': name, 'Names': [name], 'LoadState': 'loaded', 'ActiveState': 'inactive', }, []) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/timedated.py0000644000175100001710000000330000000000000023646 0ustar00runnerdocker00000000000000'''systemd timedated mock template This creates the expected methods and properties of the main org.freedesktop.timedate object. You can specify D-Bus property values like "Timezone" or "NTP" in "parameters". ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Iain Lane' __copyright__ = '(c) 2013 Canonical Ltd.' import dbus BUS_NAME = 'org.freedesktop.timedate1' MAIN_OBJ = '/org/freedesktop/timedate1' MAIN_IFACE = 'org.freedesktop.timedate1' SYSTEM_BUS = True def setProperty(prop): return f'self.Set("{MAIN_IFACE}", "{prop}", args[0])' def load(mock, parameters): mock.AddMethods(MAIN_IFACE, [ # There's nothing this can usefully do, but provide it for compatibility ('SetTime', 'xbb', '', ''), ('SetTimezone', 'sb', '', setProperty('Timezone')), ('SetLocalRTC', 'bbb', '', setProperty('LocalRTC')), ('SetNTP', 'bb', '', setProperty('NTP') + '; ' + setProperty('NTPSynchronized')) ]) mock.AddProperties(MAIN_IFACE, dbus.Dictionary({ 'Timezone': parameters.get('Timezone', 'Etc/Utc'), 'LocalRTC': parameters.get('LocalRTC', False), 'NTP': parameters.get('NTP', True), 'NTPSynchronized': parameters.get('NTP', True), 'CanNTP': parameters.get('CanNTP', True) }, signature='sv')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/upower.py0000644000175100001710000002256100000000000023241 0ustar00runnerdocker00000000000000'''upowerd mock template This creates the expected methods and properties of the main org.freedesktop.UPower object, but no devices. You can specify any property such as 'OnLowBattery' or the return value of 'SuspendAllowed', 'HibernateAllowed', and 'GetCriticalAction' in "parameters". This provides the 1.0 D-Bus API of upower. ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012, 2013 Canonical Ltd.' import dbus from dbusmock import MOCK_IFACE, mockobject import dbusmock BUS_NAME = 'org.freedesktop.UPower' MAIN_OBJ = '/org/freedesktop/UPower' MAIN_IFACE = 'org.freedesktop.UPower' SYSTEM_BUS = True DEVICE_IFACE = 'org.freedesktop.UPower.Device' def load(mock, parameters): mock.AddMethods(MAIN_IFACE, [ ('EnumerateDevices', '', 'ao', 'ret = [k for k in objects.keys() ' 'if "/devices" in k and not k.endswith("/DisplayDevice")]'), ]) props = dbus.Dictionary({ 'DaemonVersion': parameters.get('DaemonVersion', '0.99'), 'OnBattery': parameters.get('OnBattery', False), 'LidIsPresent': parameters.get('LidIsPresent', True), 'LidIsClosed': parameters.get('LidIsClosed', False), 'LidForceSleep': parameters.get('LidForceSleep', True), 'IsDocked': parameters.get('IsDocked', False), }, signature='sv') mock.AddMethods(MAIN_IFACE, [ ('GetCriticalAction', '', 's', f'ret = "{parameters.get("GetCriticalAction", "HybridSleep")}"'), ('GetDisplayDevice', '', 'o', 'ret = "/org/freedesktop/UPower/devices/DisplayDevice"') ]) mock.p_display_dev = '/org/freedesktop/UPower/devices/DisplayDevice' # add Display device; for defined properties, see # http://cgit.freedesktop.org/upower/tree/src/org.freedesktop.UPower.xml mock.AddObject(mock.p_display_dev, DEVICE_IFACE, { 'Type': dbus.UInt32(0, variant_level=1), 'State': dbus.UInt32(0, variant_level=1), 'Percentage': dbus.Double(0.0, variant_level=1), 'Energy': dbus.Double(0.0, variant_level=1), 'EnergyFull': dbus.Double(0.0, variant_level=1), 'EnergyRate': dbus.Double(0.0, variant_level=1), 'TimeToEmpty': dbus.Int64(0, variant_level=1), 'TimeToFull': dbus.Int64(0, variant_level=1), 'IsPresent': dbus.Boolean(False, variant_level=1), 'IconName': dbus.String('', variant_level=1), # LEVEL_NONE 'WarningLevel': dbus.UInt32(1, variant_level=1), }, [ ('Refresh', '', '', ''), ]) mock.AddProperties(MAIN_IFACE, props) @dbus.service.method(MOCK_IFACE, in_signature='ss', out_signature='s') def AddAC(self, device_name, model_name): '''Convenience method to add an AC object You have to specify a device name which must be a valid part of an object path, e. g. "mock_ac", and an arbitrary model name. Please note that this does not set any global properties such as "on-battery". Returns the new object path. ''' path = '/org/freedesktop/UPower/devices/' + device_name self.AddObject(path, DEVICE_IFACE, { 'PowerSupply': dbus.Boolean(True, variant_level=1), 'Model': dbus.String(model_name, variant_level=1), 'Online': dbus.Boolean(True, variant_level=1), }, []) self.EmitSignal(MAIN_IFACE, 'DeviceAdded', 'o', [path]) return path @dbus.service.method(MOCK_IFACE, in_signature='ssdx', out_signature='s') def AddDischargingBattery(self, device_name, model_name, percentage, seconds_to_empty): '''Convenience method to add a discharging battery object You have to specify a device name which must be a valid part of an object path, e. g. "mock_ac", an arbitrary model name, the charge percentage, and the seconds until the battery is empty. Please note that this does not set any global properties such as "on-battery". Returns the new object path. ''' path = '/org/freedesktop/UPower/devices/' + device_name self.AddObject(path, DEVICE_IFACE, { 'PowerSupply': dbus.Boolean(True, variant_level=1), 'IsPresent': dbus.Boolean(True, variant_level=1), 'Model': dbus.String(model_name, variant_level=1), 'Percentage': dbus.Double(percentage, variant_level=1), 'TimeToEmpty': dbus.Int64(seconds_to_empty, variant_level=1), 'EnergyFull': dbus.Double(100.0, variant_level=1), 'Energy': dbus.Double(percentage, variant_level=1), # UP_DEVICE_STATE_DISCHARGING 'State': dbus.UInt32(2, variant_level=1), # UP_DEVICE_KIND_BATTERY 'Type': dbus.UInt32(2, variant_level=1), }, []) self.EmitSignal(MAIN_IFACE, 'DeviceAdded', 'o', [path]) return path @dbus.service.method(MOCK_IFACE, in_signature='ssdx', out_signature='s') def AddChargingBattery(self, device_name, model_name, percentage, seconds_to_full): '''Convenience method to add a charging battery object You have to specify a device name which must be a valid part of an object path, e. g. "mock_ac", an arbitrary model name, the charge percentage, and the seconds until the battery is full. Please note that this does not set any global properties such as "on-battery". Returns the new object path. ''' path = '/org/freedesktop/UPower/devices/' + device_name self.AddObject(path, DEVICE_IFACE, { 'PowerSupply': dbus.Boolean(True, variant_level=1), 'IsPresent': dbus.Boolean(True, variant_level=1), 'Model': dbus.String(model_name, variant_level=1), 'Percentage': dbus.Double(percentage, variant_level=1), 'TimeToFull': dbus.Int64(seconds_to_full, variant_level=1), 'EnergyFull': dbus.Double(100.0, variant_level=1), 'Energy': dbus.Double(percentage, variant_level=1), # UP_DEVICE_STATE_CHARGING 'State': dbus.UInt32(1, variant_level=1), # UP_DEVICE_KIND_BATTERY 'Type': dbus.UInt32(2, variant_level=1), }, []) self.EmitSignal(MAIN_IFACE, 'DeviceAdded', 'o', [path]) return path @dbus.service.method(MOCK_IFACE, in_signature='uuddddxxbsu', out_signature='') def SetupDisplayDevice(self, _type, state, percentage, energy, energy_full, energy_rate, time_to_empty, time_to_full, is_present, icon_name, warning_level): '''Convenience method to configure DisplayDevice properties This calls Set() for all properties that the DisplayDevice is defined to have, and is shorter if you have to completely set it up instead of changing just one or two properties. ''' display_props = mockobject.objects[self.p_display_dev] display_props.Set(DEVICE_IFACE, 'Type', dbus.UInt32(_type)) display_props.Set(DEVICE_IFACE, 'State', dbus.UInt32(state)) display_props.Set(DEVICE_IFACE, 'Percentage', percentage) display_props.Set(DEVICE_IFACE, 'Energy', energy) display_props.Set(DEVICE_IFACE, 'EnergyFull', energy_full) display_props.Set(DEVICE_IFACE, 'EnergyRate', energy_rate) display_props.Set(DEVICE_IFACE, 'TimeToEmpty', dbus.Int64(time_to_empty)) display_props.Set(DEVICE_IFACE, 'TimeToFull', dbus.Int64(time_to_full)) display_props.Set(DEVICE_IFACE, 'IsPresent', is_present) display_props.Set(DEVICE_IFACE, 'IconName', icon_name) display_props.Set(DEVICE_IFACE, 'WarningLevel', dbus.UInt32(warning_level)) @dbus.service.method(MOCK_IFACE, in_signature='oa{sv}', out_signature='') def SetDeviceProperties(_self, object_path, properties): '''Convenience method to Set a device's properties. object_path: the device to update properties: dictionary of keys to dbus variants. Changing this property will trigger the device's PropertiesChanged signal. ''' device = dbusmock.get_object(object_path) # set the properties for key, value in properties.items(): device.Set(DEVICE_IFACE, key, value) @dbus.service.method(MOCK_IFACE, in_signature='o', out_signature='') def RemoveDevice(self, device_path): self.RemoveObject(device_path) self.EmitSignal(MAIN_IFACE, 'DeviceRemoved', 'o', [device_path]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/templates/urfkill.py0000644000175100001710000000707200000000000023370 0ustar00runnerdocker00000000000000'''urfkill mock template This creates the expected methods and properties of the main urfkill object, but no devices. You can specify any property such as urfkill in "parameters". ''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Jussi Pakkanen' __copyright__ = '(C) 2015 Canonical ltd' import dbus import dbusmock SYSTEM_BUS = True BUS_NAME = 'org.freedesktop.URfkill' MAIN_OBJ = '/org/freedesktop/URfkill' MAIN_IFACE = 'org.freedesktop.URfkill' individual_objects = ['BLUETOOTH', 'FM', 'GPS', 'NFC', 'UWB', 'WIMAX', 'WLAN', 'WWAN'] type2objectname = { 1: 'WLAN', 2: 'BLUETOOTH', 3: 'UWB', 4: 'WIMAX', 5: 'WWAN', 6: 'GPS', 7: 'FM', } KS_NOTAVAILABLE = -1 KS_UNBLOCKED = 0 KS_SOFTBLOCKED = 1 KS_HARDBLOCKED = 2 def toggle_flight_mode(self, new_block_state): new_block_state = bool(new_block_state) if self.flight_mode == new_block_state: return True self.flight_mode = new_block_state for i in individual_objects: old_value = self.internal_states[i] if old_value == 1: continue # It was already blocked so we don't need to do anything path = '/org/freedesktop/URfkill/' + i obj = dbusmock.get_object(path) if new_block_state: obj.Set('org.freedesktop.URfkill.Killswitch', 'state', 1) obj.EmitSignal('org.freedesktop.URfkill.Killswitch', 'StateChanged', '', []) else: obj.Set('org.freedesktop.URfkill.Killswitch', 'state', 0) obj.EmitSignal('org.freedesktop.URfkill.Killswitch', 'StateChanged', '', []) self.EmitSignal(MAIN_IFACE, 'FlightModeChanged', 'b', [self.flight_mode]) return True def block(self, index, should_block): should_block = bool(should_block) if index not in type2objectname: return False objname = type2objectname[index] if should_block: new_block_state = 1 else: new_block_state = 0 if self.internal_states[objname] != new_block_state: path = '/org/freedesktop/URfkill/' + objname obj = dbusmock.get_object(path) self.internal_states[objname] = new_block_state obj.Set('org.freedesktop.URfkill.Killswitch', 'state', new_block_state) obj.EmitSignal('org.freedesktop.URfkill.Killswitch', 'StateChanged', '', []) return True def load(mock, parameters): mock.toggle_flight_mode = toggle_flight_mode mock.block = block mock.flight_mode = False mock.internal_states = {} for oname in individual_objects: mock.internal_states[oname] = KS_UNBLOCKED # First we create the main urfkill object. mock.AddMethods(MAIN_IFACE, [ ('IsFlightMode', '', 'b', 'ret = self.flight_mode'), ('FlightMode', 'b', 'b', 'ret = self.toggle_flight_mode(self, args[0])'), ('Block', 'ub', 'b', 'ret = self.block(self, args[0], args[1])'), ]) mock.AddProperties(MAIN_IFACE, dbus.Dictionary({ 'DaemonVersion': parameters.get('DaemonVersion', '0.6.0'), 'KeyControl': parameters.get('KeyControl', True) }, signature='sv')) for i in individual_objects: path = '/org/freedesktop/URfkill/' + i mock.AddObject(path, 'org.freedesktop.URfkill.Killswitch', {'state': mock.internal_states[i]}, []) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/dbusmock/testcase.py0000644000175100001710000003112200000000000021526 0ustar00runnerdocker00000000000000'''unittest.TestCase convenience methods for DBusMocks''' # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import errno import os import shutil import signal import subprocess import sys import tempfile import time import unittest from typing import Tuple, Dict, Any import dbus from dbusmock.mockobject import MOCK_IFACE, OBJECT_MANAGER_IFACE, load_module class DBusTestCase(unittest.TestCase): '''Base class for D-Bus mock tests. This provides some convenience API to start/stop local D-Buses, so that you can run a private local session and/or system bus to run mocks on. This also provides a spawn_server() static method to run the D-Bus mock server in a separate process. ''' session_bus_pid = None system_bus_pid = None _DBusTestCase__datadir = '' @classmethod def get_services_dir(cls, system_bus: bool = False) -> str: '''Returns the private services directory for the bus type in question. This allows dropping in a .service file so that the dbus server inside dbusmock can launch it. ''' # NOTE: Explicitly use the attribute of DBusTestCase, as cls may be a # different class depending on how the method is called. if system_bus: services_dir = 'system_services' else: services_dir = 'services' if not DBusTestCase._DBusTestCase__datadir: DBusTestCase._DBusTestCase__datadir = tempfile.mkdtemp(prefix='dbusmock_data_') cls.addClassCleanup(setattr, DBusTestCase, '_DBusTestCase__datadir', '') cls.addClassCleanup(shutil.rmtree, DBusTestCase._DBusTestCase__datadir) os.mkdir(os.path.join(DBusTestCase._DBusTestCase__datadir, 'system_services')) os.mkdir(os.path.join(DBusTestCase._DBusTestCase__datadir, 'services')) return os.path.join(DBusTestCase._DBusTestCase__datadir, services_dir) @classmethod def __start_bus(cls, bus_type) -> None: '''Set up a private local session bus This gets stopped automatically at class teardown. ''' cls.get_services_dir() with open(os.path.join(DBusTestCase._DBusTestCase__datadir, f'dbusmock_{bus_type}_cfg'), 'w', encoding='ascii') as c: c.write(f''' {bus_type} unix:tmpdir=/tmp {cls.get_services_dir(bus_type == 'system')} ''') c.flush() (pid, addr) = cls.start_dbus(conf=c.name) os.environ[f'DBUS_{bus_type.upper()}_BUS_ADDRESS'] = addr setattr(cls, f'{bus_type}_bus_pid', pid) cls.addClassCleanup(setattr, cls, f'{bus_type}_bus_pid', None) cls.addClassCleanup(os.environ.pop, f'DBUS_{bus_type.upper()}_BUS_ADDRESS') cls.addClassCleanup(cls.stop_dbus, pid) @classmethod def start_session_bus(cls) -> None: '''Set up a private local session bus This gets stopped automatically at class teardown. ''' DBusTestCase.__start_bus('session') @classmethod def start_system_bus(cls) -> None: '''Set up a private local system bus This gets stopped automatically at class teardown. ''' DBusTestCase.__start_bus('system') @classmethod def start_dbus(cls, conf: str = None) -> Tuple[int, str]: '''Start a D-Bus daemon Return (pid, address) pair. Normally you do not need to call this directly. Use start_system_bus() and start_session_bus() instead. ''' argv = ['dbus-daemon', '--fork', '--print-address=1', '--print-pid=1'] if conf: argv.append('--config-file=' + conf) else: argv.append('--session') lines = subprocess.check_output(argv, universal_newlines=True).strip().splitlines() assert len(lines) == 2, 'expected exactly 2 lines of output from dbus-daemon' # usually the first line is the address, but be lenient and accept any order try: return (int(lines[1]), lines[0]) except ValueError: return (int(lines[0]), lines[1]) @classmethod def stop_dbus(cls, pid: int) -> None: '''Stop a D-Bus daemon Normally you do not need to call this directly. When you use start_system_bus() and start_session_bus(), these buses are automatically stopped in tearDownClass(). ''' signal.signal(signal.SIGTERM, signal.SIG_IGN) for _ in range(50): try: os.kill(pid, signal.SIGTERM) except OSError as e: if e.errno == errno.ESRCH: break raise time.sleep(0.1) else: sys.stderr.write('ERROR: timed out waiting for bus process to terminate\n') os.kill(pid, signal.SIGKILL) time.sleep(0.5) signal.signal(signal.SIGTERM, signal.SIG_DFL) @classmethod def get_dbus(cls, system_bus: bool = False) -> dbus.Bus: '''Get dbus.bus.BusConnection() object This is preferrable to dbus.SystemBus() and dbus.SessionBus() as those do not get along with multiple changing local test buses. ''' if system_bus: if os.environ.get('DBUS_SYSTEM_BUS_ADDRESS'): return dbus.bus.BusConnection(os.environ['DBUS_SYSTEM_BUS_ADDRESS']) return dbus.SystemBus() if os.environ.get('DBUS_SESSION_BUS_ADDRESS'): return dbus.bus.BusConnection(os.environ['DBUS_SESSION_BUS_ADDRESS']) return dbus.SessionBus() @classmethod def wait_for_bus_object(cls, dest: str, path: str, system_bus: bool = False, timeout: int = 600): '''Wait for an object to appear on D-Bus Raise an exception if object does not appear within one minute. You can change the timeout with the "timeout" keyword argument which specifies deciseconds. ''' bus = cls.get_dbus(system_bus) last_exc = None # we check whether the name is owned first, to avoid race conditions # with service activation; once it's owned, wait until we can actually # call methods while timeout > 0: if bus.name_has_owner(dest): try: p = dbus.Interface(bus.get_object(dest, path), dbus_interface=dbus.INTROSPECTABLE_IFACE) p.Introspect() break except dbus.exceptions.DBusException as e: last_exc = e if '.UnknownInterface' in str(e): break timeout -= 1 time.sleep(0.1) if timeout <= 0: assert timeout > 0, f'timed out waiting for D-Bus object {path}: {last_exc}' @classmethod def spawn_server(cls, name: str, path: str, interface: str, system_bus: bool = False, stdout: int = None): '''Run a DBusMockObject instance in a separate process The daemon will terminate automatically when the D-Bus that it connects to goes down. If that does not happen (e. g. you test on the actual system/session bus), you need to kill it manually. This function blocks until the spawned DBusMockObject is ready and listening on the bus. Returns the Popen object of the spawned daemon. ''' argv = [sys.executable, '-m', 'dbusmock'] if system_bus: argv.append('--system') argv.append(name) argv.append(path) argv.append(interface) # pylint: disable=consider-using-with daemon = subprocess.Popen(argv, stdout=stdout) # wait for daemon to start up cls.wait_for_bus_object(name, path, system_bus) return daemon @classmethod def spawn_server_template(cls, template: str, parameters: Dict[str, Any] = None, stdout: int = None, system_bus: bool = None): '''Run a D-Bus mock template instance in a separate process This starts a D-Bus mock process and loads the given template with (optional) parameters into it. For details about templates see dbusmock.DBusMockObject.AddTemplate(). Usually a template should specify SYSTEM_BUS = False/True to select whether it gets loaded on the session or system bus. This can be overridden with the system_bus parameter. For templates which don't set SYSTEM_BUS, this parameter has to be set. The daemon will terminate automatically when the D-Bus that it connects to goes down. If that does not happen (e. g. you test on the actual system/session bus), you need to kill it manually. This function blocks until the spawned DBusMockObject is ready and listening on the bus. Returns a pair (daemon Popen object, main dbus object). ''' # we need the bus address from the template module module = load_module(template) if hasattr(module, 'IS_OBJECT_MANAGER'): is_object_manager = module.IS_OBJECT_MANAGER else: is_object_manager = False if is_object_manager and not hasattr(module, 'MAIN_IFACE'): interface_name = OBJECT_MANAGER_IFACE else: interface_name = module.MAIN_IFACE if system_bus is None: system_bus = module.SYSTEM_BUS daemon = cls.spawn_server(module.BUS_NAME, module.MAIN_OBJ, interface_name, system_bus, stdout) bus = cls.get_dbus(system_bus) obj = bus.get_object(module.BUS_NAME, module.MAIN_OBJ) if not parameters: parameters = dbus.Dictionary({}, signature='sv') obj.AddTemplate(template, parameters, dbus_interface=MOCK_IFACE) return (daemon, obj) @classmethod def enable_service(cls, service, system_bus: bool = False) -> None: '''Enable the given well known service name inside dbusmock This symlinks a service file from the usual dbus service directories into the dbusmock environment. Doing that allows the service to be launched automatically if they are defined within $XDG_DATA_DIRS. The daemon configuration is reloaded if a test bus is running. ''' services_dir = 'system-services' if system_bus else 'services' xdg_data_dirs = os.environ.get('XDG_DATA_DIRS') or '/usr/local/share/:/usr/share/' for d in xdg_data_dirs.split(':'): src = os.path.join(d, 'dbus-1', services_dir, service + '.service') if os.path.exists(src): os.symlink(src, os.path.join(cls.get_services_dir(system_bus), service + '.service')) break else: raise AssertionError(f"Service {service} not found in XDG_DATA_DIRS ({xdg_data_dirs})") dbus_pid = cls.system_bus_pid if system_bus else cls.session_bus_pid if dbus_pid: bus = cls.get_dbus(system_bus) dbus_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') dbus_if = dbus.Interface(dbus_obj, 'org.freedesktop.DBus') dbus_if.ReloadConfig() @classmethod def disable_service(cls, service, system_bus: bool = False) -> None: '''Disable the given well known service name inside dbusmock This unlink's the .service file for the service and reloads the daemon configuration if a test bus is running. ''' try: os.unlink(os.path.join(cls.get_services_dir(system_bus), service + '.service')) except OSError: raise AssertionError(f"Service {service} not found") from None dbus_pid = cls.system_bus_pid if system_bus else cls.session_bus_pid if dbus_pid: bus = cls.get_dbus(system_bus) dbus_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') dbus_if = dbus.Interface(dbus_obj, 'org.freedesktop.DBus') dbus_if.ReloadConfig() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649137868.5371122 python-dbusmock-0.27.5/python_dbusmock.egg-info/0000755000175100001710000000000000000000000022435 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137868.0 python-dbusmock-0.27.5/python_dbusmock.egg-info/PKG-INFO0000644000175100001710000003451400000000000023541 0ustar00runnerdocker00000000000000Metadata-Version: 2.1 Name: python-dbusmock Version: 0.27.5 Summary: Mock D-Bus objects Home-page: https://github.com/martinpitt/python-dbusmock Author: Martin Pitt Author-email: martin.pitt@ubuntu.com License: LGPL 3+ Download-URL: https://pypi.python.org/pypi/python-dbusmock/ Description: python-dbusmock =============== ## Purpose With this program/Python library you can easily create mock objects on D-Bus. This is useful for writing tests for software which talks to D-Bus services such as upower, systemd, logind, gnome-session or others, and it is hard (or impossible without root privileges) to set the state of the real services to what you expect in your tests. Suppose you want to write tests for gnome-settings-daemon's power plugin, or another program that talks to upower. You want to verify that after the configured idle time the program suspends the machine. So your program calls `org.freedesktop.UPower.Suspend()` on the system D-Bus. Now, your test suite should not really talk to the actual system D-Bus and the real upower; a `make check` that suspends your machine will not be considered very friendly by most people, and if you want to run this in continuous integration test servers or package build environments, chances are that your process does not have the privilege to suspend, or there is no system bus or upower to begin with. Likewise, there is no way for an user process to forcefully set the system/seat idle flag in logind, so your tests cannot set up the expected test environment on the real daemon. That's where mock objects come into play: They look like the real API (or at least the parts that you actually need), but they do not actually do anything (or only some action that you specify yourself). You can configure their state, behaviour and responses as you like in your test, without making any assumptions about the real system status. When using a local system/session bus, you can do unit or integration testing without needing root privileges or disturbing a running system. The Python API offers some convenience functions like `start_session_bus()` and `start_system_bus()` for this, in a `DBusTestCase` class (subclass of the standard `unittest.TestCase`). You can use this with any programming language, as you can run the mocker as a normal program. The actual setup of the mock (adding objects, methods, properties, and signals) all happen via D-Bus methods on the `org.freedesktop.DBus.Mock` interface. You just don't have the convenience D-Bus launch API that way. ## Simple example in Python Picking up the above example about mocking upower's `Suspend()` method, this is how you would set up a mock upower in your test case: ```python import dbus import dbusmock class TestMyProgram(dbusmock.DBusTestCase): @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(system_bus=True) def setUp(self): self.p_mock = self.spawn_server('org.freedesktop.UPower', '/org/freedesktop/UPower', 'org.freedesktop.UPower', system_bus=True, stdout=subprocess.PIPE) # Get a proxy for the UPower object's Mock interface self.dbus_upower_mock = dbus.Interface(self.dbus_con.get_object( 'org.freedesktop.UPower', '/org/freedesktop/UPower'), dbusmock.MOCK_IFACE) self.dbus_upower_mock.AddMethod('', 'Suspend', '', '', '') def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_suspend_on_idle(self): # run your program in a way that should trigger one suspend call # now check the log that we got one Suspend() call self.assertRegex(self.p_mock.stdout.readline(), b'^[0-9.]+ Suspend$') ``` Let's walk through: - We derive our tests from `dbusmock.DBusTestCase` instead of `unittest.TestCase` directly, to make use of the convenience API to start a local system bus. - `setUpClass()` starts a local system bus, and makes a connection to it available to all methods as `dbus_con`. `True` means that we connect to the system bus, not the session bus. We can use the same bus for all tests, so doing this once in `setUpClass()` instead of `setUp()` is enough. - `setUp()` spawns the mock D-Bus server process for an initial `/org/freedesktop/UPower` object with an `org.freedesktop.UPower` D-Bus interface on the system bus. We capture its stdout to be able to verify that methods were called. We then call `org.freedesktop.DBus.Mock.AddMethod()` to add a `Suspend()` method to our new object to the default D-Bus interface. This will not do anything (except log its call to stdout). It takes no input arguments, returns nothing, and does not run any custom code. - `tearDown()` stops our mock D-Bus server again. We do this so that each test case has a fresh and clean upower instance, but of course you can also set up everything in `setUpClass()` if tests do not interfere with each other on setting up the mock. - `test_suspend_on_idle()` is the actual test case. It needs to run your program in a way that should trigger one suspend call. Your program will try to call `Suspend()`, but as that's now being served by our mock instead of upower, there will not be any actual machine suspend. Our mock process will log the method call together with a time stamp; you can use the latter for doing timing related tests, but we just ignore it here. ## Simple example from shell We use the actual session bus for this example. You can use `dbus-run-session` to start a private one as well if you want, but that is not part of the actual mocking. So let's start a mock at the D-Bus name `com.example.Foo` with an initial "main" object on path /, with the main D-Bus interface `com.example.Foo.Manager`: python3 -m dbusmock com.example.Foo / com.example.Foo.Manager On another terminal, let's first see what it does: gdbus introspect --session -d com.example.Foo -o / You'll see that it supports the standard D-Bus `Introspectable` and `Properties` interfaces, as well as the `org.freedesktop.DBus.Mock` interface for controlling the mock, but no "real" functionality yet. So let's add a method: gdbus call --session -d com.example.Foo -o / -m org.freedesktop.DBus.Mock.AddMethod '' Ping '' '' '' Now you can see the new method in `introspect`, and call it: gdbus call --session -d com.example.Foo -o / -m com.example.Foo.Manager.Ping The mock process in the other terminal will log the method call with a time stamp, and you'll see something like `1348832614.970 Ping`. Now add another method with two int arguments and a return value and call it: gdbus call --session -d com.example.Foo -o / -m org.freedesktop.DBus.Mock.AddMethod \ '' Add 'ii' 'i' 'ret = args[0] + args[1]' gdbus call --session -d com.example.Foo -o / -m com.example.Foo.Manager.Add 2 3 This will print `(5,)` as expected (remember that the return value is always a tuple), and again the mock process will log the Add method call. You can do the same operations in e. g. d-feet or any other D-Bus language binding. ## Logging Usually you want to verify which methods have been called on the mock with which arguments. There are three ways to do that: - By default, the mock process writes the call log to stdout. - You can call the mock process with the `-l`/`--logfile` argument, or specify a log file object in the `spawn_server()` method if you are using Python. - You can use the `GetCalls()`, `GetMethodCalls()` and `ClearCalls()` methods on the `org.freedesktop.DBus.Mock` D-Bus interface to get an array of tuples describing the calls. ## Templates Some D-Bus services are commonly used in test suites, such as UPower or NetworkManager. python-dbusmock provides "templates" which set up the common structure of these services (their main objects, properties, and methods) so that you do not need to carry around this common code, and only need to set up the particular properties and specific D-Bus objects that you need. These templates can be parameterized for common customizations, and they can provide additional convenience methods on the `org.freedesktop.DBus.Mock` interface to provide more abstract functionality like "add a battery". For example, for starting a server with the `upower` template in Python you can run (self.p_mock, self.obj_upower) = self.spawn_server_template( 'upower', {'OnBattery': True}, stdout=subprocess.PIPE) or load a template into an already running server with the `AddTemplate()` method; this is particularly useful if you are not using Python: python3 -m dbusmock --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower gdbus call --system -d org.freedesktop.UPower -o /org/freedesktop/UPower -m org.freedesktop.DBus.Mock.AddTemplate 'upower' '{"OnBattery": }' This creates all expected properties such as `DaemonVersion`, and changes the default for one of them (`OnBattery`) through the (optional) parameters dict. If you do not need to specify parameters, you can do this in a simpler way with python3 -m dbusmock --template upower The template does not create any devices by default. You can add some with the template's convenience methods like ac_path = self.dbusmock.AddAC('mock_AC', 'Mock AC') bt_path = self.dbusmock.AddChargingBattery('mock_BAT', 'Mock Battery', 30.0, 1200) or calling `AddObject()` yourself with the desired properties, of course. Templates commonly implement some non-trivial functionality with actual Python methods and the standard [dbus-python](https://dbus.freedesktop.org/doc/dbus-python/) [`@dbus.service.method`](https://dbus.freedesktop.org/doc/dbus-python/dbus.service.html#dbus.service.method) decorator. If you want to contribute a template, look at dbusmock/templates/upower.py for a real-life implementation. You can copy dbusmock/templates/SKELETON to your new template file name and replace `CHANGEME` with the actual code/values. ## More Examples Have a look at the test suite for two real-live use cases: - `tests/test_upower.py` simulates upowerd, in a more complete way than in above example and using the `upower` template. It verifies that `upower --dump` is convinced that it's talking to upower. - `tests/test_api.py` runs a mock on the session bus and exercises all available functionality, such as adding additional objects, properties, multiple methods, input arguments, return values, code in methods, sending signals, raising exceptions, and introspection. ## Documentation The `dbusmock` module has extensive documentation built in, which you can read with e. g. `pydoc3 dbusmock`. `pydoc3 dbusmock.DBusMockObject` shows the D-Bus API of the mock object, i. e. methods like `AddObject()`, `AddMethod()` etc. which are used to set up your mock object. `pydoc3 dbusmock.DBusTestCase` shows the convenience Python API for writing test cases with local private session/system buses and launching the server. `pydoc3 dbusmock.templates` shows all available templates. `pydoc3 dbusmock.templates.NAME` shows the documentation and available parameters for the `NAME` template. `python3 -m dbusmock --help` shows the arguments and options for running the mock server as a program. ## Development python-dbusmock is hosted on https://github.com/martinpitt/python-dbusmock Run the unit tests with python3 -m unittest Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Other Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX :: BSD Classifier: Operating System :: Unix Classifier: Topic :: Software Development :: Quality Assurance Classifier: Topic :: Software Development :: Testing Classifier: Topic :: Software Development :: Libraries :: Python Modules Description-Content-Type: text/markdown ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137868.0 python-dbusmock-0.27.5/python_dbusmock.egg-info/SOURCES.txt0000644000175100001710000000251100000000000024320 0ustar00runnerdocker00000000000000COPYING MANIFEST.in NEWS README.md setup.cfg setup.py dbusmock/__init__.py dbusmock/__main__.py dbusmock/mockobject.py dbusmock/testcase.py dbusmock/templates/__init__.py dbusmock/templates/accounts_service.py dbusmock/templates/bluez5-obex.py dbusmock/templates/bluez5.py dbusmock/templates/gnome_screensaver.py dbusmock/templates/iio-sensors-proxy.py dbusmock/templates/logind.py dbusmock/templates/low_memory_monitor.py dbusmock/templates/networkmanager.py dbusmock/templates/notification_daemon.py dbusmock/templates/ofono.py dbusmock/templates/polkitd.py dbusmock/templates/power_profiles_daemon.py dbusmock/templates/systemd.py dbusmock/templates/timedated.py dbusmock/templates/upower.py dbusmock/templates/urfkill.py python_dbusmock.egg-info/PKG-INFO python_dbusmock.egg-info/SOURCES.txt python_dbusmock.egg-info/dependency_links.txt python_dbusmock.egg-info/top_level.txt tests/__init__.py tests/test_accounts_service.py tests/test_api.py tests/test_bluez5.py tests/test_cli.py tests/test_code.py tests/test_gnome_screensaver.py tests/test_iio_sensors_proxy.py tests/test_logind.py tests/test_low_memory_monitor.py tests/test_networkmanager.py tests/test_notification_daemon.py tests/test_ofono.py tests/test_polkitd.py tests/test_power_profiles_daemon.py tests/test_systemd.py tests/test_timedated.py tests/test_upower.py tests/test_urfkill.py././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137868.0 python-dbusmock-0.27.5/python_dbusmock.egg-info/dependency_links.txt0000644000175100001710000000000100000000000026503 0ustar00runnerdocker00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137868.0 python-dbusmock-0.27.5/python_dbusmock.egg-info/top_level.txt0000644000175100001710000000001100000000000025157 0ustar00runnerdocker00000000000000dbusmock ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649137868.5451124 python-dbusmock-0.27.5/setup.cfg0000644000175100001710000000030000000000000017345 0ustar00runnerdocker00000000000000[mypy] [mypy-dbus.*] ignore_missing_imports = True [mypy-gi.repository] ignore_missing_imports = True [mypy-setuptools] ignore_missing_imports = True [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/setup.py0000755000175100001710000000261000000000000017247 0ustar00runnerdocker00000000000000#!/usr/bin/python3 '''python-dbusmock - Mock D-Bus objects for testing''' import setuptools with open('README.md', encoding="UTF-8") as f: readme = f.read() with open('NEWS', 'r', encoding="UTF-8") as f: version = f.readline().split('[')[1].split(']')[0] setuptools.setup( name='python-dbusmock', version=version, description='Mock D-Bus objects', long_description=readme, long_description_content_type='text/markdown', author='Martin Pitt', author_email='martin.pitt@ubuntu.com', url='https://github.com/martinpitt/python-dbusmock', download_url='https://pypi.python.org/pypi/python-dbusmock/', license='LGPL 3+', packages=['dbusmock', 'dbusmock.templates'], classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Development Status :: 3 - Alpha', 'Environment :: Other Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)', 'Operating System :: POSIX :: Linux', 'Operating System :: POSIX :: BSD', 'Operating System :: Unix', 'Topic :: Software Development :: Quality Assurance', 'Topic :: Software Development :: Testing', 'Topic :: Software Development :: Libraries :: Python Modules', ], ) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649137868.5411122 python-dbusmock-0.27.5/tests/0000755000175100001710000000000000000000000016675 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/__init__.py0000644000175100001710000000000000000000000020774 0ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_accounts_service.py0000644000175100001710000003250300000000000023650 0ustar00runnerdocker00000000000000#!/usr/bin/python3 """ Tests for accounts service """ # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Marco Trevisan' __copyright__ = '(c) 2021 Canonical Ltd.' import subprocess import sys import time import unittest import dbus import dbusmock try: import gi # type: ignore gi.require_version('AccountsService', '1.0') from gi.repository import AccountsService, GLib have_accounts_service = True except (ImportError, ValueError): have_accounts_service = False @unittest.skipUnless(have_accounts_service, 'AccountsService gi introspection not available') class TestAccountsService(dbusmock.DBusTestCase): '''Test mocking AccountsService''' @classmethod def setUpClass(cls): super().setUpClass() cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) cls.ctx = GLib.main_context_default() def setUp(self): super().setUp() (self.p_mock, self.p_obj) = self.spawn_server_template( 'accounts_service', {}, stdout=subprocess.PIPE) self.p_manager = AccountsService.UserManager.get_default() while not self.p_manager.props.is_loaded: self.ctx.iteration(True) self.assertFalse(self.p_manager.no_service()) self.assertTrue(self.p_manager.props.is_loaded) def get_property(self, name): return self.p_obj.Get('org.freedesktop.Accounts', name, dbus_interface=dbus.PROPERTIES_IFACE) def tearDown(self): for user in self.p_manager.list_users(): self.p_manager.delete_user(user, False) while self.p_manager.list_users(): self.ctx.iteration(True) if self.p_mock: self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() while self.p_manager.props.is_loaded: self.ctx.iteration(True) self.assertFalse(self.p_manager.props.is_loaded) del self.p_manager super().tearDown() def wait_changed(self, user): changed = False def on_changed(u): nonlocal changed changed = u is user conn_id = user.connect('changed', on_changed) while not changed: self.ctx.iteration(True) user.disconnect(conn_id) def test_empty(self): self.assertTrue(self.p_manager.props.is_loaded) self.assertFalse(self.p_manager.list_users()) self.assertFalse(self.p_manager.props.has_multiple_users) self.assertFalse(self.p_obj.ListMockUsers()) self.assertTrue(self.get_property('HasNoUsers')) self.assertFalse(self.get_property('HasMultipleUsers')) self.assertFalse(self.get_property('AutomaticLoginUsers')) self.assertEqual(self.get_property('DaemonVersion'), 'dbus-mock-0.1') def test_create_users(self): self.p_manager.create_user( 'pizza', 'I Love Pizza', AccountsService.UserAccountType.ADMINISTRATOR) creation_time = int(time.time()) self.assertFalse(self.p_manager.props.has_multiple_users) self.assertFalse(self.get_property('HasNoUsers')) [user] = self.p_manager.list_users() self.assertEqual([user.get_object_path()], self.p_obj.ListMockUsers()) self.assertEqual(user.get_account_type(), AccountsService.UserAccountType.ADMINISTRATOR) self.assertFalse(user.get_automatic_login()) self.assertEqual(user.get_email(), 'pizza@python-dbusmock.org') self.assertEqual(user.get_home_dir(), '/nonexisting/mock-home/pizza') self.assertEqual(user.get_icon_file(), '') self.assertEqual(user.get_language(), 'C') self.assertEqual(user.get_location(), '') self.assertFalse(user.get_locked()) self.assertEqual(user.get_login_frequency(), 0) self.assertEqual(user.get_login_history().unpack(), []) self.assertEqual(user.get_login_time(), 0) self.assertEqual(user.get_num_sessions(), 0) self.assertEqual(user.get_num_sessions_anywhere(), 0) self.assertEqual(user.get_object_path(), '/org/freedesktop/Accounts/User2001') self.assertEqual(user.get_password_expiration_policy(), (sys.maxsize, creation_time, 0, 0, 0, 0)) self.assertEqual(user.get_password_hint(), 'Remember it, come on!') self.assertEqual(user.get_password_mode(), AccountsService.UserPasswordMode.REGULAR) self.assertIsNone(user.get_primary_session_id()) self.assertEqual(user.get_real_name(), 'I Love Pizza') self.assertFalse(user.get_saved()) self.assertEqual(user.get_session(), 'mock-session') self.assertEqual(user.get_session_type(), 'wayland') self.assertEqual(user.get_shell(), '/usr/bin/zsh') self.assertEqual(user.get_uid(), 2001) self.assertEqual(user.get_user_name(), 'pizza') self.assertEqual(user.get_x_session(), 'mock-xsession') self.assertTrue(user.is_loaded()) self.assertTrue(user.is_local_account()) self.assertFalse(user.is_logged_in()) self.assertFalse(user.is_logged_in_anywhere()) self.assertFalse(user.is_nonexistent()) self.assertFalse(user.is_system_account()) other = self.p_manager.create_user( 'schiacciata', 'I Love Schiacciata too', AccountsService.UserAccountType.STANDARD) while not self.p_manager.props.has_multiple_users: self.ctx.iteration(True) self.assertTrue(self.p_manager.props.has_multiple_users) self.assertFalse(self.get_property('HasNoUsers')) self.assertIn(other, self.p_manager.list_users()) self.assertEqual(other.get_uid(), 2002) self.assertEqual(other.get_user_name(), 'schiacciata') self.assertEqual(other.get_real_name(), 'I Love Schiacciata too') self.assertEqual(other.get_account_type(), AccountsService.UserAccountType.STANDARD) self.assertEqual( [u.get_object_path() for u in self.p_manager.list_users()], self.p_obj.ListMockUsers()) def test_recreate_user(self): self.p_manager.create_user( 'pizza', 'I Love Pizza', AccountsService.UserAccountType.ADMINISTRATOR) self.assertFalse(self.p_manager.props.has_multiple_users) self.assertFalse(self.get_property('HasNoUsers')) with self.assertRaises(GLib.Error) as error: self.p_manager.create_user( 'pizza', 'More and more', AccountsService.UserAccountType.STANDARD) self.assertTrue(error.exception.matches( AccountsService.UserManagerError.quark(), AccountsService.UserManagerError.FAILED)) self.assertFalse(self.p_manager.props.has_multiple_users) def test_set_user_properties(self): # pylint: disable=too-many-statements user = self.p_manager.create_user( 'test-user', 'I am a Test user', AccountsService.UserAccountType.STANDARD) user.set_account_type( AccountsService.UserAccountType.ADMINISTRATOR) self.wait_changed(user) self.assertEqual(user.get_account_type(), AccountsService.UserAccountType.ADMINISTRATOR) user.set_account_type( AccountsService.UserAccountType.STANDARD) self.wait_changed(user) self.assertEqual(user.get_account_type(), AccountsService.UserAccountType.STANDARD) user.set_automatic_login(True) self.wait_changed(user) self.assertTrue(user.get_automatic_login()) user.set_automatic_login(False) self.wait_changed(user) self.assertFalse(user.get_automatic_login()) user.set_email('test@email.org') self.wait_changed(user) self.assertEqual(user.get_email(), 'test@email.org') user.set_icon_file('/nonexistant/home/icon.png') self.wait_changed(user) self.assertEqual(user.get_icon_file(), '/nonexistant/home/icon.png') user.set_language('Test Language') self.wait_changed(user) self.assertEqual(user.get_language(), 'Test Language') user.set_location('Test Location') self.wait_changed(user) self.assertEqual(user.get_location(), 'Test Location') user.set_locked(True) self.wait_changed(user) self.assertTrue(user.get_locked()) user.set_locked(False) self.wait_changed(user) self.assertFalse(user.get_locked()) user.set_password('Test Password', 'Test PasswordHint') self.wait_changed(user) self.assertEqual(user.get_password_hint(), 'Test PasswordHint') user.set_password_mode(AccountsService.UserPasswordMode.NONE) self.wait_changed(user) self.assertEqual(user.get_password_mode(), AccountsService.UserPasswordMode.NONE) user.set_password_mode(AccountsService.UserPasswordMode.REGULAR) self.wait_changed(user) self.assertEqual(user.get_password_mode(), AccountsService.UserPasswordMode.REGULAR) user.set_password_mode(AccountsService.UserPasswordMode.SET_AT_LOGIN) self.wait_changed(user) self.assertEqual(user.get_password_mode(), AccountsService.UserPasswordMode.SET_AT_LOGIN) user.set_real_name('Test RealName') self.wait_changed(user) self.assertEqual(user.get_real_name(), 'Test RealName') user.set_session('Test Session') self.wait_changed(user) self.assertEqual(user.get_session(), 'Test Session') user.set_session_type('Test SessionType') self.wait_changed(user) self.assertEqual(user.get_session_type(), 'Test SessionType') user.set_user_name('new-test-user') self.wait_changed(user) self.assertEqual(user.get_user_name(), 'new-test-user') user.set_x_session('Test XSession') self.wait_changed(user) self.assertEqual(user.get_x_session(), 'Test XSession') def test_automatic_login_users(self): user = self.p_manager.create_user( 'test-user', 'I am a Test user', AccountsService.UserAccountType.STANDARD) user.set_automatic_login(True) self.wait_changed(user) self.assertTrue(user.get_automatic_login()) self.assertEqual( [user.get_object_path()], self.get_property('AutomaticLoginUsers')) self.assertCountEqual(self.p_obj.ListMockUsers(), self.get_property('AutomaticLoginUsers')) user2 = self.p_manager.create_user( 'another-test-user', 'I am another Test user', AccountsService.UserAccountType.STANDARD) self.assertNotIn(user2.get_object_path(), self.get_property('AutomaticLoginUsers')) user2.set_automatic_login(True) self.assertIn(user2.get_object_path(), self.get_property('AutomaticLoginUsers')) self.assertEqual(len(self.get_property('AutomaticLoginUsers')), 2) self.assertCountEqual(self.p_obj.ListMockUsers(), self.get_property('AutomaticLoginUsers')) user.set_automatic_login(False) self.wait_changed(user) self.assertFalse(user.get_automatic_login()) self.assertNotIn(user.get_object_path(), self.get_property('AutomaticLoginUsers')) self.assertEqual(len(self.get_property('AutomaticLoginUsers')), 1) self.p_manager.delete_user(user2, False) while user2 in self.p_manager.list_users(): self.ctx.iteration(True) self.assertFalse(self.get_property('AutomaticLoginUsers')) def test_automatic_login_users_via_mock(self): with self.assertRaises(dbus.exceptions.DBusException): self.p_obj.AddAutoLoginUser('non-existant') with self.assertRaises(dbus.exceptions.DBusException): self.p_obj.RemoveAutoLoginUser('non-existant') user = self.p_manager.create_user( 'test-user', 'I am a Test user', AccountsService.UserAccountType.STANDARD) self.p_obj.AddAutoLoginUser(user.get_user_name()) self.wait_changed(user) self.assertTrue(user.get_automatic_login()) self.assertIn(user.get_object_path(), self.get_property('AutomaticLoginUsers')) # Adding again to check we don't get confused! self.p_obj.AddAutoLoginUser(user.get_user_name()) self.assertTrue(user.get_automatic_login()) self.assertCountEqual([user.get_object_path()], self.get_property('AutomaticLoginUsers')) self.p_obj.RemoveAutoLoginUser(user.get_user_name()) self.wait_changed(user) self.assertFalse(user.get_automatic_login()) self.assertNotIn(user.get_object_path(), self.get_property('AutomaticLoginUsers')) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner( stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_api.py0000644000175100001710000012405300000000000021064 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import unittest import sys import os import tempfile import subprocess import shutil import time import importlib.util import tracemalloc import dbus import dbus.mainloop.glib from gi.repository import GLib import dbusmock tracemalloc.start(25) dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) # "a b" in py2/3 compatible unicode UNICODE = b'a\xe2\x99\xa5b'.decode('UTF-8') class TestAPI(dbusmock.DBusTestCase): '''Test dbus-mock API''' @classmethod def setUpClass(cls): cls.start_session_bus() cls.dbus_con = cls.get_dbus() def setUp(self): # pylint: disable=consider-using-with self.mock_log = tempfile.NamedTemporaryFile() self.p_mock = self.spawn_server('org.freedesktop.Test', '/', 'org.freedesktop.Test.Main', stdout=self.mock_log) self.obj_test = self.dbus_con.get_object('org.freedesktop.Test', '/') self.dbus_test = dbus.Interface(self.obj_test, 'org.freedesktop.Test.Main') self.dbus_mock = dbus.Interface(self.obj_test, dbusmock.MOCK_IFACE) self.dbus_props = dbus.Interface(self.obj_test, dbus.PROPERTIES_IFACE) def assertLog(self, regex): with open(self.mock_log.name, "rb") as f: self.assertRegex(f.read(), regex) def tearDown(self): if self.p_mock.stdout: self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_noarg_noret(self): '''no arguments, no return value''' self.dbus_mock.AddMethod('', 'Do', '', '', '') self.assertEqual(self.dbus_test.Do(), None) # check that it's logged correctly self.assertLog(b'^[0-9.]+ Do$') def test_onearg_noret(self): '''one argument, no return value''' self.dbus_mock.AddMethod('', 'Do', 's', '', '') self.assertEqual(self.dbus_test.Do('Hello'), None) # check that it's logged correctly self.assertLog(b'^[0-9.]+ Do "Hello"$') def test_onearg_ret(self): '''one argument, code for return value''' self.dbus_mock.AddMethod('', 'Do', 's', 's', 'ret = args[0]') self.assertEqual(self.dbus_test.Do('Hello'), 'Hello') def test_unicode_str(self): '''unicode string roundtrip''' self.dbus_mock.AddMethod('', 'Do', 's', 's', 'ret = args[0] * 2') self.assertEqual(self.dbus_test.Do(UNICODE), dbus.String(UNICODE * 2)) def test_twoarg_ret(self): '''two arguments, code for return value''' self.dbus_mock.AddMethod('', 'Do', 'si', 's', 'ret = args[0] * args[1]') self.assertEqual(self.dbus_test.Do('foo', 3), 'foofoofoo') # check that it's logged correctly self.assertLog(b'^[0-9.]+ Do "foo" 3$') def test_array_arg(self): '''array argument''' self.dbus_mock.AddMethod('', 'Do', 'iaous', '', f'''assert len(args) == 4 assert args[0] == -1; assert args[1] == ['/foo'] assert type(args[1]) == dbus.Array assert type(args[1][0]) == dbus.ObjectPath assert args[2] == 5 assert args[3] == {repr(UNICODE)} ''') self.assertEqual(self.dbus_test.Do(-1, ['/foo'], 5, UNICODE), None) # check that it's logged correctly self.assertLog(b'^[0-9.]+ Do -1 \\["/foo"\\] 5 "a\\xe2\\x99\\xa5b"$') def test_dict_arg(self): '''dictionary argument''' self.dbus_mock.AddMethod('', 'Do', 'ia{si}u', '', '''assert len(args) == 3 assert args[0] == -1; assert args[1] == {'foo': 42} assert type(args[1]) == dbus.Dictionary assert args[2] == 5 ''') self.assertEqual(self.dbus_test.Do(-1, {'foo': 42}, 5), None) # check that it's logged correctly self.assertLog(b'^[0-9.]+ Do -1 {"foo": 42} 5$') def test_exception(self): '''raise a D-Bus exception''' self.dbus_mock.AddMethod('', 'Do', '', 'i', 'raise dbus.exceptions.DBusException("no good", name="com.example.Error.NoGood")') with self.assertRaises(dbus.exceptions.DBusException) as cm: self.dbus_test.Do() self.assertEqual(cm.exception.get_dbus_name(), 'com.example.Error.NoGood') self.assertEqual(cm.exception.get_dbus_message(), 'no good') self.assertLog(b'\n[0-9.]+ Do raised: com.example.Error.NoGood:.*\n') def test_methods_on_other_interfaces(self): '''methods on other interfaces''' self.dbus_mock.AddMethod('org.freedesktop.Test.Other', 'OtherDo', '', '', '') self.dbus_mock.AddMethods('org.freedesktop.Test.Other', [('OtherDo2', '', '', ''), ('OtherDo3', 'i', 'i', 'ret = args[0]')]) # should not be on the main interface self.assertRaises(dbus.exceptions.DBusException, self.dbus_test.OtherDo) # should be on the other interface self.assertEqual(self.obj_test.OtherDo(dbus_interface='org.freedesktop.Test.Other'), None) self.assertEqual(self.obj_test.OtherDo2(dbus_interface='org.freedesktop.Test.Other'), None) self.assertEqual(self.obj_test.OtherDo3(42, dbus_interface='org.freedesktop.Test.Other'), 42) # check that it's logged correctly self.assertLog(b'^[0-9.]+ OtherDo\n[0-9.]+ OtherDo2\n[0-9.]+ OtherDo3 42$') def test_methods_same_name(self): '''methods with same name on different interfaces''' self.dbus_mock.AddMethod('org.iface1', 'Do', 'i', 'i', 'ret = args[0] + 2') self.dbus_mock.AddMethod('org.iface2', 'Do', 'i', 'i', 'ret = args[0] + 3') # should not be on the main interface self.assertRaises(dbus.exceptions.DBusException, self.dbus_test.Do) # should be on the other interface self.assertEqual(self.obj_test.Do(10, dbus_interface='org.iface1'), 12) self.assertEqual(self.obj_test.Do(11, dbus_interface='org.iface2'), 14) # check that it's logged correctly self.assertLog(b'^[0-9.]+ Do 10\n[0-9.]+ Do 11$') # now add it to the primary interface, too self.dbus_mock.AddMethod('', 'Do', 'i', 'i', 'ret = args[0] + 1') self.assertEqual(self.obj_test.Do(9, dbus_interface='org.freedesktop.Test.Main'), 10) self.assertEqual(self.obj_test.Do(10, dbus_interface='org.iface1'), 12) self.assertEqual(self.obj_test.Do(11, dbus_interface='org.iface2'), 14) def test_methods_type_mismatch(self): '''calling methods with wrong arguments''' def check(signature, args, err): self.dbus_mock.AddMethod('', 'Do', signature, '', '') try: self.dbus_test.Do(*args) self.fail(f'method call did not raise an error for signature "{signature}" and arguments {args}') except dbus.exceptions.DBusException as e: self.assertEqual(e.get_dbus_name(), 'org.freedesktop.DBus.Error.InvalidArgs') self.assertIn(err, str(e)) # not enough arguments check('i', [], 'More items found') check('is', [1], 'More items found') # too many arguments check('', [1], 'Fewer items found') check('i', [1, 'hello'], 'Fewer items found') # type mismatch check('u', [-1], 'convert negative value to unsigned') check('i', ['hello'], 'dbus.String') check('i', ['hello'], 'integer') check('s', [1], 'Expected a string') def test_add_object(self): '''add a new object''' self.dbus_mock.AddObject('/obj1', 'org.freedesktop.Test.Sub', { 'state': dbus.String('online', variant_level=1), 'cute': dbus.Boolean(True, variant_level=1), }, []) obj1 = self.dbus_con.get_object('org.freedesktop.Test', '/obj1') dbus_sub = dbus.Interface(obj1, 'org.freedesktop.Test.Sub') dbus_props = dbus.Interface(obj1, dbus.PROPERTIES_IFACE) # check properties self.assertEqual(dbus_props.Get('org.freedesktop.Test.Sub', 'state'), 'online') self.assertEqual(dbus_props.Get('org.freedesktop.Test.Sub', 'cute'), True) self.assertEqual(dbus_props.GetAll('org.freedesktop.Test.Sub'), {'state': 'online', 'cute': True}) # add new method obj1.AddMethod('', 'Do', '', 's', 'ret = "hello"', dbus_interface=dbusmock.MOCK_IFACE) self.assertEqual(dbus_sub.Do(), 'hello') def test_add_object_existing(self): '''try to add an existing object''' self.dbus_mock.AddObject('/obj1', 'org.freedesktop.Test.Sub', {}, []) self.assertRaises(dbus.exceptions.DBusException, self.dbus_mock.AddObject, '/obj1', 'org.freedesktop.Test.Sub', {}, []) # try to add the main object again self.assertRaises(dbus.exceptions.DBusException, self.dbus_mock.AddObject, '/', 'org.freedesktop.Test.Other', {}, []) def test_add_object_with_methods(self): '''add a new object with methods''' self.dbus_mock.AddObject('/obj1', 'org.freedesktop.Test.Sub', { 'state': dbus.String('online', variant_level=1), 'cute': dbus.Boolean(True, variant_level=1), }, [ ('Do0', '', 'i', 'ret = 42'), ('Do1', 'i', 'i', 'ret = 31337'), ]) obj1 = self.dbus_con.get_object('org.freedesktop.Test', '/obj1') self.assertEqual(obj1.Do0(), 42) self.assertEqual(obj1.Do1(1), 31337) self.assertRaises(dbus.exceptions.DBusException, obj1.Do2, 31337) def test_properties(self): '''add and change properties''' # no properties by default self.assertEqual(self.dbus_props.GetAll('org.freedesktop.Test.Main'), {}) # no such property with self.assertRaises(dbus.exceptions.DBusException) as ctx: self.dbus_props.Get('org.freedesktop.Test.Main', 'version') self.assertEqual(ctx.exception.get_dbus_name(), 'org.freedesktop.Test.Main.UnknownProperty') self.assertEqual(ctx.exception.get_dbus_message(), 'no such property version') self.assertRaises(dbus.exceptions.DBusException, self.dbus_props.Set, 'org.freedesktop.Test.Main', 'version', dbus.Int32(2, variant_level=1)) self.dbus_mock.AddProperty('org.freedesktop.Test.Main', 'version', dbus.Int32(2, variant_level=1)) # once again on default interface self.dbus_mock.AddProperty('', 'connected', dbus.Boolean(True, variant_level=1)) self.assertEqual(self.dbus_props.Get('org.freedesktop.Test.Main', 'version'), 2) self.assertEqual(self.dbus_props.Get('org.freedesktop.Test.Main', 'connected'), True) self.assertEqual(self.dbus_props.GetAll('org.freedesktop.Test.Main'), {'version': 2, 'connected': True}) with self.assertRaises(dbus.exceptions.DBusException) as ctx: self.dbus_props.GetAll('org.freedesktop.Test.Bogus') self.assertEqual(ctx.exception.get_dbus_name(), 'org.freedesktop.Test.Main.UnknownInterface') self.assertEqual(ctx.exception.get_dbus_message(), 'no such interface org.freedesktop.Test.Bogus') # change property self.dbus_props.Set('org.freedesktop.Test.Main', 'version', dbus.Int32(4, variant_level=1)) self.assertEqual(self.dbus_props.Get('org.freedesktop.Test.Main', 'version'), 4) # check that the Get/Set calls get logged with open(self.mock_log.name, encoding="UTF-8") as f: contents = f.read() self.assertRegex(contents, '\n[0-9.]+ Get / org.freedesktop.Test.Main.version\n') self.assertRegex(contents, '\n[0-9.]+ Get / org.freedesktop.Test.Main.connected\n') self.assertRegex(contents, '\n[0-9.]+ GetAll / org.freedesktop.Test.Main\n') self.assertRegex(contents, '\n[0-9.]+ Set / org.freedesktop.Test.Main.version 4\n') # add property to different interface self.dbus_mock.AddProperty('org.freedesktop.Test.Other', 'color', dbus.String('yellow', variant_level=1)) self.assertEqual(self.dbus_props.GetAll('org.freedesktop.Test.Main'), {'version': 4, 'connected': True}) self.assertEqual(self.dbus_props.GetAll('org.freedesktop.Test.Other'), {'color': 'yellow'}) self.assertEqual(self.dbus_props.Get('org.freedesktop.Test.Other', 'color'), 'yellow') changed_props = [] ml = GLib.MainLoop() def catch(*args, **kwargs): if kwargs['interface'] != 'org.freedesktop.DBus.Properties': return self.assertEqual(kwargs['interface'], 'org.freedesktop.DBus.Properties') self.assertEqual(kwargs['member'], 'PropertiesChanged') [iface, changed, _invalidated] = args self.assertEqual(iface, 'org.freedesktop.Test.Main') changed_props.append(changed) ml.quit() match = self.dbus_con.add_signal_receiver(catch, interface_keyword='interface', path_keyword='path', member_keyword='member') # change property using mock helper self.dbus_mock.UpdateProperties('org.freedesktop.Test.Main', { 'version': 5, 'connected': False, }) GLib.timeout_add(3000, ml.quit) ml.run() match.remove() self.assertEqual(self.dbus_props.GetAll('org.freedesktop.Test.Main'), {'version': 5, 'connected': False}) self.assertEqual(changed_props, [{'version': 5, 'connected': False}]) # test adding properties with the array type self.dbus_mock.AddProperty('org.freedesktop.Test.Main', 'array', dbus.Array(['first'], signature='s')) self.assertEqual(self.dbus_props.Get('org.freedesktop.Test.Main', 'array'), ['first']) # test updating properties with the array type self.dbus_mock.UpdateProperties('org.freedesktop.Test.Main', {'array': dbus.Array(['second', 'third'], signature='s')}) self.assertEqual(self.dbus_props.Get('org.freedesktop.Test.Main', 'array'), ['second', 'third']) def test_introspection_methods(self): '''dynamically added methods appear in introspection''' dbus_introspect = dbus.Interface(self.obj_test, dbus.INTROSPECTABLE_IFACE) xml_empty = dbus_introspect.Introspect() self.assertIn('', xml_empty) self.assertIn('', xml_empty) self.dbus_mock.AddMethod('', 'Do', 'saiv', 'i', 'ret = 42') xml_method = dbus_introspect.Introspect() self.assertNotEqual(xml_empty, xml_method) self.assertIn('', xml_method) # various Python versions use different name vs. type ordering expected1 = ''' ''' expected2 = ''' ''' self.assertTrue(expected1 in xml_method or expected2 in xml_method, xml_method) # properties in introspection are not supported by dbus-python right now def test_introspection_properties(self): '''dynamically added properties appear in introspection''' self.dbus_mock.AddProperty('', 'Color', 'yellow') self.dbus_mock.AddProperty('org.freedesktop.Test.Sub', 'Count', 5) xml = self.obj_test.Introspect() self.assertIn('', xml) self.assertIn('', xml) # various Python versions use different attribute ordering self.assertTrue('' in xml or '' in xml, xml) self.assertTrue('' in xml or '' in xml, xml) def test_objects_map(self): '''access global objects map''' self.dbus_mock.AddMethod('', 'EnumObjs', '', 'ao', 'ret = objects.keys()') self.assertEqual(self.dbus_test.EnumObjs(), ['/']) self.dbus_mock.AddObject('/obj1', 'org.freedesktop.Test.Sub', {}, []) self.assertEqual(set(self.dbus_test.EnumObjs()), {'/', '/obj1'}) def test_signals(self): '''emitting signals''' def do_emit(): self.dbus_mock.EmitSignal('', 'SigNoArgs', '', []) self.dbus_mock.EmitSignal('org.freedesktop.Test.Sub', 'SigTwoArgs', 'su', ['hello', 42]) self.dbus_mock.EmitSignal('org.freedesktop.Test.Sub', 'SigTypeTest', 'iuvao', [-42, 42, dbus.String('hello', variant_level=1), ['/a', '/b']]) caught = [] ml = GLib.MainLoop() def catch(*args, **kwargs): if kwargs['interface'].startswith('org.freedesktop.Test'): caught.append((args, kwargs)) if len(caught) == 3: # we caught everything there is to catch, don't wait for the # timeout ml.quit() self.dbus_con.add_signal_receiver(catch, interface_keyword='interface', path_keyword='path', member_keyword='member') GLib.timeout_add(200, do_emit) # ensure that the loop quits even when we catch fewer than 2 signals GLib.timeout_add(3000, ml.quit) ml.run() # check SigNoArgs self.assertEqual(caught[0][0], ()) self.assertEqual(caught[0][1]['member'], 'SigNoArgs') self.assertEqual(caught[0][1]['path'], '/') self.assertEqual(caught[0][1]['interface'], 'org.freedesktop.Test.Main') # check SigTwoArgs self.assertEqual(caught[1][0], ('hello', 42)) self.assertEqual(caught[1][1]['member'], 'SigTwoArgs') self.assertEqual(caught[1][1]['path'], '/') self.assertEqual(caught[1][1]['interface'], 'org.freedesktop.Test.Sub') # check data types in SigTypeTest self.assertEqual(caught[2][1]['member'], 'SigTypeTest') self.assertEqual(caught[2][1]['path'], '/') args = caught[2][0] self.assertEqual(args[0], -42) self.assertEqual(type(args[0]), dbus.Int32) self.assertEqual(args[0].variant_level, 0) self.assertEqual(args[1], 42) self.assertEqual(type(args[1]), dbus.UInt32) self.assertEqual(args[1].variant_level, 0) self.assertEqual(args[2], 'hello') self.assertEqual(type(args[2]), dbus.String) self.assertEqual(args[2].variant_level, 1) self.assertEqual(args[3], ['/a', '/b']) self.assertEqual(type(args[3]), dbus.Array) self.assertEqual(args[3].variant_level, 0) self.assertEqual(type(args[3][0]), dbus.ObjectPath) self.assertEqual(args[3][0].variant_level, 0) # check correct logging with open(self.mock_log.name, encoding="UTF-8") as f: log = f.read() self.assertRegex(log, '[0-9.]+ emit / org.freedesktop.Test.Main.SigNoArgs\n') self.assertRegex(log, '[0-9.]+ emit / org.freedesktop.Test.Sub.SigTwoArgs "hello" 42\n') self.assertRegex(log, '[0-9.]+ emit / org.freedesktop.Test.Sub.SigTypeTest -42 42') self.assertRegex(log, r'[0-9.]+ emit / org.freedesktop.Test.Sub.SigTypeTest -42 42 "hello" \["/a", "/b"\]\n') def test_signals_type_mismatch(self): '''emitting signals with wrong arguments''' def check(signature, args, err): try: self.dbus_mock.EmitSignal('', 's', signature, args) self.fail(f'EmitSignal did not raise an error for signature "{signature}" and arguments {args}') except dbus.exceptions.DBusException as e: self.assertEqual(e.get_dbus_name(), 'org.freedesktop.DBus.Error.InvalidArgs') self.assertIn(err, str(e)) # not enough arguments check('i', [], 'More items found') check('is', [1], 'More items found') # too many arguments check('', [1], 'Fewer items found') check('i', [1, 'hello'], 'Fewer items found') # type mismatch check('u', [-1], 'convert negative value to unsigned') check('i', ['hello'], 'dbus.String') check('i', ['hello'], 'integer') check('s', [1], 'Expected a string') def test_dbus_get_log(self): '''query call logs over D-Bus''' self.assertEqual(self.dbus_mock.ClearCalls(), None) self.assertEqual(self.dbus_mock.GetCalls(), dbus.Array([])) self.dbus_mock.AddMethod('', 'Do', '', '', '') self.assertEqual(self.dbus_test.Do(), None) mock_log = self.dbus_mock.GetCalls() self.assertEqual(len(mock_log), 1) self.assertGreater(mock_log[0][0], 10000) # timestamp self.assertEqual(mock_log[0][1], 'Do') self.assertEqual(mock_log[0][2], []) self.assertEqual(self.dbus_mock.ClearCalls(), None) self.assertEqual(self.dbus_mock.GetCalls(), dbus.Array([])) self.dbus_mock.AddMethod('', 'Wop', 's', 's', 'ret="hello"') self.assertEqual(self.dbus_test.Wop('foo'), 'hello') self.assertEqual(self.dbus_test.Wop('bar'), 'hello') mock_log = self.dbus_mock.GetCalls() self.assertEqual(len(mock_log), 2) self.assertGreater(mock_log[0][0], 10000) # timestamp self.assertEqual(mock_log[0][1], 'Wop') self.assertEqual(mock_log[0][2], ['foo']) self.assertEqual(mock_log[1][1], 'Wop') self.assertEqual(mock_log[1][2], ['bar']) self.assertEqual(self.dbus_mock.ClearCalls(), None) self.assertEqual(self.dbus_mock.GetCalls(), dbus.Array([])) def test_dbus_get_method_calls(self): '''query method call logs over D-Bus''' self.dbus_mock.AddMethod('', 'Do', '', '', '') self.assertEqual(self.dbus_test.Do(), None) self.assertEqual(self.dbus_test.Do(), None) self.dbus_mock.AddMethod('', 'Wop', 's', 's', 'ret="hello"') self.assertEqual(self.dbus_test.Wop('foo'), 'hello') self.assertEqual(self.dbus_test.Wop('bar'), 'hello') mock_calls = self.dbus_mock.GetMethodCalls('Do') self.assertEqual(len(mock_calls), 2) self.assertEqual(mock_calls[0][1], []) self.assertEqual(mock_calls[1][1], []) mock_calls = self.dbus_mock.GetMethodCalls('Wop') self.assertEqual(len(mock_calls), 2) self.assertGreater(mock_calls[0][0], 10000) # timestamp self.assertEqual(mock_calls[0][1], ['foo']) self.assertGreater(mock_calls[1][0], 10000) # timestamp self.assertEqual(mock_calls[1][1], ['bar']) def test_dbus_method_called(self): '''subscribe to MethodCalled signal''' loop = GLib.MainLoop() caught_signals = [] def method_called(method, args, **_): caught_signals.append((method, args)) loop.quit() self.dbus_mock.AddMethod('', 'Do', 's', '', '') self.dbus_mock.connect_to_signal('MethodCalled', method_called) self.assertEqual(self.dbus_test.Do('foo'), None) GLib.timeout_add(5000, loop.quit) loop.run() self.assertEqual(len(caught_signals), 1) method, args = caught_signals[0] self.assertEqual(method, 'Do') self.assertEqual(len(args), 1) self.assertEqual(args[0], 'foo') def test_reset(self): '''resetting to pristine state''' self.dbus_mock.AddMethod('', 'Do', '', '', '') self.dbus_mock.AddProperty('', 'propone', True) self.dbus_mock.AddProperty('org.Test.Other', 'proptwo', 1) self.dbus_mock.AddObject('/obj1', '', {}, []) self.dbus_mock.Reset() # resets properties and keeps the initial object self.assertEqual(self.dbus_props.GetAll(''), {}) # resets methods self.assertRaises(dbus.exceptions.DBusException, self.dbus_test.Do) # resets other objects obj1 = self.dbus_con.get_object('org.freedesktop.Test', '/obj1') self.assertRaises(dbus.exceptions.DBusException, obj1.GetAll, '') class TestTemplates(dbusmock.DBusTestCase): '''Test template API''' @classmethod def setUpClass(cls): cls.start_session_bus() cls.start_system_bus() def test_local(self): '''Load a local template *.py file''' with tempfile.NamedTemporaryFile(prefix='answer_', suffix='.py') as my_template: my_template.write(b'''import dbus BUS_NAME = 'universe.Ultimate' MAIN_OBJ = '/' MAIN_IFACE = 'universe.Ultimate' SYSTEM_BUS = False def load(mock, parameters): mock.AddMethods(MAIN_IFACE, [('Answer', 's', 'i', 'ret = 42')]) ''') my_template.flush() (p_mock, dbus_ultimate) = self.spawn_server_template( my_template.name, stdout=subprocess.PIPE) self.addCleanup(p_mock.wait) self.addCleanup(p_mock.terminate) self.addCleanup(p_mock.stdout.close) # ensure that we don't use/write any .pyc files, they are dangerous # in a world-writable directory like /tmp self.assertFalse(os.path.exists(my_template.name + 'c')) self.assertFalse(os.path.exists(importlib.util.cache_from_source(my_template.name))) loop = GLib.MainLoop() caught_signals = [] def method_called(method, args, **_): caught_signals.append((method, args)) loop.quit() dbus_mock = dbus.Interface(dbus_ultimate, dbusmock.MOCK_IFACE) dbus_mock.connect_to_signal('MethodCalled', method_called) self.assertEqual(dbus_ultimate.Answer("foo"), 42) self.assertEqual(dbus_ultimate.Answer("bar"), 42) # should appear in introspection xml = dbus_ultimate.Introspect() self.assertIn('', xml) self.assertIn('', xml) # should not have ObjectManager API by default self.assertRaises(dbus.exceptions.DBusException, dbus_ultimate.GetManagedObjects) # Call should have been registered mock_calls = dbus_mock.GetMethodCalls('Answer') self.assertEqual(len(mock_calls), 2) self.assertEqual(mock_calls[0][1], ['foo']) self.assertEqual(mock_calls[1][1], ['bar']) # Check signals GLib.timeout_add(5000, loop.quit) loop.run() # only one signal because we call loop.quit() in the handler self.assertEqual(len(caught_signals), 1) method, args = caught_signals[0] self.assertEqual(method, 'Answer') self.assertEqual(len(args), 1) self.assertEqual(args[0], 'foo') def test_static_method(self): '''Static method in a template''' with tempfile.NamedTemporaryFile(prefix='answer_', suffix='.py') as my_template: my_template.write(b'''import dbus BUS_NAME = 'universe.Ultimate' MAIN_OBJ = '/' MAIN_IFACE = 'universe.Ultimate' SYSTEM_BUS = False def load(mock, parameters): pass @dbus.service.method(MAIN_IFACE, in_signature='s', out_signature='i') def Answer(self, string): return 42 ''') my_template.flush() (p_mock, dbus_ultimate) = self.spawn_server_template( my_template.name, stdout=subprocess.PIPE) self.addCleanup(p_mock.wait) self.addCleanup(p_mock.terminate) self.addCleanup(p_mock.stdout.close) loop = GLib.MainLoop() caught_signals = [] def method_called(method, args, **_): caught_signals.append((method, args)) loop.quit() dbus_mock = dbus.Interface(dbus_ultimate, dbusmock.MOCK_IFACE) dbus_mock.connect_to_signal('MethodCalled', method_called) self.assertEqual(dbus_ultimate.Answer("foo"), 42) self.assertEqual(dbus_ultimate.Answer("bar"), 42) # should appear in introspection xml = dbus_ultimate.Introspect() self.assertIn('', xml) self.assertIn('', xml) # Call should have been registered mock_calls = dbus_mock.GetMethodCalls('Answer') self.assertEqual(len(mock_calls), 2) self.assertEqual(mock_calls[0][1], ['foo']) self.assertEqual(mock_calls[1][1], ['bar']) # Check signals GLib.timeout_add(5000, loop.quit) loop.run() # only one signal because we call loop.quit() in the handler self.assertEqual(len(caught_signals), 1) method, args = caught_signals[0] self.assertEqual(method, 'Answer') self.assertEqual(len(args), 1) self.assertEqual(args[0], 'foo') def test_local_nonexisting(self): self.assertRaises(ImportError, self.spawn_server_template, '/non/existing.py') def test_explicit_bus_(self): '''Explicitly set the bus for a template that does not specify SYSTEM_BUS''' with tempfile.NamedTemporaryFile(prefix='answer_', suffix='.py') as my_template: my_template.write(b'''import dbus BUS_NAME = 'universe.Ultimate' MAIN_OBJ = '/' MAIN_IFACE = 'universe.Ultimate' def load(mock, parameters): mock.AddMethods(MAIN_IFACE, [('Answer', '', 'i', 'ret = 42')]) ''') my_template.flush() (p_mock, dbus_ultimate) = self.spawn_server_template( my_template.name, stdout=subprocess.PIPE, system_bus=False) self.addCleanup(p_mock.wait) self.addCleanup(p_mock.terminate) self.addCleanup(p_mock.stdout.close) self.wait_for_bus_object('universe.Ultimate', '/') self.assertEqual(dbus_ultimate.Answer(), 42) def test_override_bus_(self): '''Override the bus for a template''' with tempfile.NamedTemporaryFile(prefix='answer_', suffix='.py') as my_template: my_template.write(b'''import dbus BUS_NAME = 'universe.Ultimate' MAIN_OBJ = '/' MAIN_IFACE = 'universe.Ultimate' SYSTEM_BUS = True def load(mock, parameters): mock.AddMethods(MAIN_IFACE, [('Answer', '', 'i', 'ret = 42')]) ''') my_template.flush() (p_mock, dbus_ultimate) = self.spawn_server_template( my_template.name, stdout=subprocess.PIPE, system_bus=False) self.addCleanup(p_mock.wait) self.addCleanup(p_mock.terminate) self.addCleanup(p_mock.stdout.close) self.wait_for_bus_object('universe.Ultimate', '/') self.assertEqual(dbus_ultimate.Answer(), 42) def test_object_manager(self): '''Template with ObjectManager API''' with tempfile.NamedTemporaryFile(prefix='objmgr_', suffix='.py') as my_template: my_template.write(b'''import dbus BUS_NAME = 'org.test.Things' MAIN_OBJ = '/org/test/Things' IS_OBJECT_MANAGER = True SYSTEM_BUS = False def load(mock, parameters): mock.AddObject('/org/test/Things/Thing1', 'org.test.Do', {'name': 'one'}, []) mock.AddObject('/org/test/Things/Thing2', 'org.test.Do', {'name': 'two'}, []) mock.AddObject('/org/test/Peer', 'org.test.Do', {'name': 'peer'}, []) ''') my_template.flush() (p_mock, dbus_objmgr) = self.spawn_server_template( my_template.name, stdout=subprocess.PIPE) self.addCleanup(p_mock.wait) self.addCleanup(p_mock.terminate) self.addCleanup(p_mock.stdout.close) # should have the two Things, but not the Peer self.assertEqual(dbus_objmgr.GetManagedObjects(), {'/org/test/Things/Thing1': {'org.test.Do': {'name': 'one'}}, '/org/test/Things/Thing2': {'org.test.Do': {'name': 'two'}}}) # should appear in introspection xml = dbus_objmgr.Introspect() self.assertIn('', xml) self.assertIn('', xml) self.assertIn('', xml) self.assertIn('', xml) def test_reset(self): '''Reset() puts the template back to pristine state''' (p_mock, obj_logind) = self.spawn_server_template( 'logind', stdout=subprocess.PIPE) self.addCleanup(p_mock.wait) self.addCleanup(p_mock.terminate) self.addCleanup(p_mock.stdout.close) # do some property, method, and object changes obj_logind.Set('org.freedesktop.login1.Manager', 'IdleAction', 'frob') mock_logind = dbus.Interface(obj_logind, dbusmock.MOCK_IFACE) mock_logind.AddProperty('org.Test.Other', 'walk', 'silly') mock_logind.AddMethod('', 'DoWalk', '', '', '') mock_logind.AddObject('/obj1', '', {}, []) mock_logind.Reset() # keeps the objects from the template dbus_con = self.get_dbus(system_bus=True) obj_logind = dbus_con.get_object('org.freedesktop.login1', '/org/freedesktop/login1') self.assertEqual(obj_logind.CanSuspend(), 'yes') # resets properties self.assertRaises(dbus.exceptions.DBusException, obj_logind.GetAll, 'org.Test.Other') self.assertEqual( obj_logind.Get('org.freedesktop.login1.Manager', 'IdleAction'), 'ignore') # resets methods self.assertRaises(dbus.exceptions.DBusException, obj_logind.DoWalk) # resets other objects obj1 = dbus_con.get_object('org.freedesktop.login1', '/obj1') self.assertRaises(dbus.exceptions.DBusException, obj1.GetAll, '') class TestCleanup(dbusmock.DBusTestCase): '''Test cleanup of resources''' def test_mock_terminates_with_bus(self): '''Spawned mock processes exit when bus goes down''' self.start_session_bus() p_mock = self.spawn_server('org.freedesktop.Test', '/', 'org.freedesktop.Test.Main') self.stop_dbus(self.session_bus_pid) # give the mock 2 seconds to terminate timeout = 20 while timeout > 0: if p_mock.poll() is not None: break timeout -= 1 time.sleep(0.1) if p_mock.poll() is None: # clean up manually p_mock.terminate() p_mock.wait() self.fail('mock process did not terminate after 2 seconds') self.assertEqual(p_mock.wait(), 0) class TestSubclass(dbusmock.DBusTestCase): '''Test subclassing DBusMockObject''' @classmethod def setUpClass(cls): cls.start_session_bus() def test_ctor(self): '''Override DBusMockObject constructor''' class MyMock(dbusmock.mockobject.DBusMockObject): def __init__(self): bus_name = dbus.service.BusName('org.test.MyMock', dbusmock.testcase.DBusTestCase.get_dbus()) dbusmock.mockobject.DBusMockObject.__init__( self, bus_name, '/', 'org.test.A', {}, os.devnull) self.AddMethod('', 'Ping', '', 'i', 'ret = 42') m = MyMock() self.assertEqual(m.Ping(), 42) # pylint: disable=no-member def test_none_props(self): '''object with None properties argument''' class MyMock(dbusmock.mockobject.DBusMockObject): def __init__(self): bus_name = dbus.service.BusName('org.test.MyMock', dbusmock.testcase.DBusTestCase.get_dbus()) dbusmock.mockobject.DBusMockObject.__init__( self, bus_name, '/mymock', 'org.test.MyMockI', None, os.devnull) self.AddMethod('', 'Ping', '', 'i', 'ret = 42') m = MyMock() self.assertEqual(m.Ping(), 42) # pylint: disable=no-member self.assertEqual(m.GetAll('org.test.MyMockI'), {}) m.AddProperty('org.test.MyMockI', 'blurb', 5) self.assertEqual(m.GetAll('org.test.MyMockI'), {'blurb': 5}) class TestServiceAutostart(dbusmock.DBusTestCase): '''Test service starting DBusMockObject''' @classmethod def setUpClass(cls): cls.xdg_data_dir = tempfile.mkdtemp(prefix='dbusmock_xdg_') cls.addClassCleanup(shutil.rmtree, cls.xdg_data_dir) os.environ['XDG_DATA_DIRS'] = cls.xdg_data_dir os.mkdir(os.path.join(cls.xdg_data_dir, 'dbus-1')) system_dir = os.path.join(cls.xdg_data_dir, 'dbus-1', 'system-services') session_dir = os.path.join(cls.xdg_data_dir, 'dbus-1', 'services') os.mkdir(system_dir) os.mkdir(session_dir) with open(os.path.join(system_dir, 'org.TestSystem.service'), 'w', encoding='ascii') as s: s.write('[D-BUS Service]\n' + 'Name=org.TestSystem\n' 'Exec=/usr/bin/python3 -c "import sys; from gi.repository import GLib, Gio; ' ' Gio.bus_own_name(Gio.BusType.SYSTEM, \'org.TestSystem\', 0, None, None, lambda *args: sys.exit(0)); ' ' GLib.MainLoop().run()"\n' 'User=root') with open(os.path.join(session_dir, 'org.TestSession.service'), 'w', encoding='ascii') as s: s.write('[D-BUS Service]\n' 'Name=org.TestSession\n' 'Exec=/usr/bin/python3 -c "import sys; from gi.repository import GLib, Gio; ' ' Gio.bus_own_name(Gio.BusType.SESSION, \'org.TestSession\', 0, None, None, lambda *args: sys.exit(0)); ' ' GLib.MainLoop().run()"\n' 'User=root') cls.start_system_bus() cls.start_session_bus() def test_session_service_function_raise(self): with self.assertRaises(AssertionError): self.enable_service('does-not-exist') with self.assertRaises(AssertionError): self.disable_service('does-not-exist') def test_session_service_isolation(self): dbus_con = self.get_dbus(system_bus=False) dbus_obj = dbus_con.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') dbus_if = dbus.Interface(dbus_obj, 'org.freedesktop.DBus') self.assertEqual(dbus_if.ListActivatableNames(), ['org.freedesktop.DBus']) self.enable_service('org.TestSession') self.addCleanup(self.disable_service, 'org.TestSession') self.assertEqual(dbus_if.ListActivatableNames(), ['org.freedesktop.DBus', 'org.TestSession']) def test_system_service_isolation(self): dbus_con = self.get_dbus(system_bus=True) dbus_obj = dbus_con.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') dbus_if = dbus.Interface(dbus_obj, 'org.freedesktop.DBus') self.assertEqual(dbus_if.ListActivatableNames(), ['org.freedesktop.DBus']) self.enable_service('org.TestSystem', system_bus=True) self.addCleanup(self.disable_service, 'org.TestSystem', system_bus=True) self.assertEqual(dbus_if.ListActivatableNames(), ['org.freedesktop.DBus', 'org.TestSystem']) def test_session_service_activation(self): dbus_con = self.get_dbus(system_bus=False) dbus_obj = dbus_con.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') dbus_if = dbus.Interface(dbus_obj, 'org.freedesktop.DBus') self.enable_service('org.TestSession') self.addCleanup(self.disable_service, 'org.TestSession') dbus_if.StartServiceByName('org.TestSession', 0) def test_system_service_activation(self): dbus_con = self.get_dbus(system_bus=True) dbus_obj = dbus_con.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') dbus_if = dbus.Interface(dbus_obj, 'org.freedesktop.DBus') self.enable_service('org.TestSystem', system_bus=True) self.addCleanup(self.disable_service, 'org.TestSystem', system_bus=True) dbus_if.StartServiceByName('org.TestSystem', 0) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_bluez5.py0000644000175100001710000002642300000000000021523 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Philip Withnall' __copyright__ = '(c) 2013 Collabora Ltd.' import os import shutil import subprocess import sys import time import unittest import tracemalloc import dbus import dbus.mainloop.glib from gi.repository import GLib import dbusmock tracemalloc.start(25) dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) have_bluetoothctl = shutil.which('bluetoothctl') have_pbap_client = shutil.which('pbap-client') def _run_bluetoothctl(command): '''Run bluetoothctl with the given command. Return its output as a list of lines, with the command prompt removed from each, and empty lines eliminated. If bluetoothctl returns a non-zero exit code, raise an Exception. ''' with subprocess.Popen(['bluetoothctl'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) as process: time.sleep(0.5) # give it time to query the bus out, err = process.communicate(input='list\n' + command + '\nquit\n') # Ignore output on stderr unless bluetoothctl dies. if process.returncode != 0: raise Exception('bluetoothctl died with status ' + str(process.returncode) + ' and errors: ' + (err or "")) # Strip the prompt from the start of every line, then remove empty # lines. # # The prompt looks like ‘[bluetooth]# ’, potentially containing command # line colour control codes. Split at the first space. # # Sometimes we end up with the final line being ‘\x1b[K’ (partial # control code), which we need to ignore. def remove_prefix(line): if line.startswith('[bluetooth]#') or line.startswith('\x1b'): parts = line.split(' ', 1) try: return parts[1].strip() except IndexError: return '' return line.strip() lines = out.split('\n') lines = map(remove_prefix, lines) lines = filter(lambda line: line != '', lines) # Filter out the echoed commands. (bluetoothctl uses readline.) lines = filter(lambda line: line not in ['list', command, 'quit'], lines) lines = list(lines) return lines @unittest.skipUnless(have_bluetoothctl, 'bluetoothctl not installed') class TestBlueZ5(dbusmock.DBusTestCase): '''Test mocking bluetoothd''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) (cls.p_mock, cls.obj_bluez) = cls.spawn_server_template( 'bluez5', {}, stdout=subprocess.PIPE) def setUp(self): self.obj_bluez.Reset() self.dbusmock = dbus.Interface(self.obj_bluez, dbusmock.MOCK_IFACE) self.dbusmock_bluez = dbus.Interface(self.obj_bluez, 'org.bluez.Mock') def test_no_adapters(self): # Check for adapters. out = _run_bluetoothctl('list') for line in out: self.assertFalse(line.startswith('Controller ')) def test_one_adapter(self): # Chosen parameters. adapter_name = 'hci0' system_name = 'my-computer' # Add an adapter path = self.dbusmock_bluez.AddAdapter(adapter_name, system_name) self.assertEqual(path, '/org/bluez/' + adapter_name) adapter = self.dbus_con.get_object('org.bluez', path) address = adapter.Get('org.bluez.Adapter1', 'Address') address_type = adapter.Get('org.bluez.Adapter1', 'AddressType') # Check for the adapter. out = _run_bluetoothctl('list') self.assertIn('Controller ' + address + ' ' + system_name + ' [default]', out) out = _run_bluetoothctl('show ' + address) if address_type is not None: self.assertIn(f'Controller {address} ({address_type})', out) else: self.assertIn('Controller ' + address, out) self.assertIn('Name: ' + system_name, out) self.assertIn('Alias: ' + system_name, out) self.assertIn('Powered: yes', out) self.assertIn('Discoverable: no', out) self.assertIn('Pairable: yes', out) self.assertIn('Discovering: no', out) def test_no_devices(self): # Add an adapter. adapter_name = 'hci0' path = self.dbusmock_bluez.AddAdapter(adapter_name, 'my-computer') self.assertEqual(path, '/org/bluez/' + adapter_name) # Check for devices. out = _run_bluetoothctl('devices') self.assertIn('Controller 00:01:02:03:04:05 my-computer [default]', out) def test_one_device(self): # Add an adapter. adapter_name = 'hci0' path = self.dbusmock_bluez.AddAdapter(adapter_name, 'my-computer') self.assertEqual(path, '/org/bluez/' + adapter_name) # Add a device. address = '11:22:33:44:55:66' alias = 'My Phone' path = self.dbusmock_bluez.AddDevice(adapter_name, address, alias) self.assertEqual(path, '/org/bluez/' + adapter_name + '/dev_' + address.replace(':', '_')) # Check for the device. out = _run_bluetoothctl('devices') self.assertIn('Device ' + address + ' ' + alias, out) # Check the device’s properties. out = '\n'.join(_run_bluetoothctl('info ' + address)) self.assertIn('Device ' + address, out) self.assertIn('Name: ' + alias, out) self.assertIn('Alias: ' + alias, out) self.assertIn('Paired: no', out) self.assertIn('Trusted: no', out) self.assertIn('Blocked: no', out) self.assertIn('Connected: no', out) def test_pairing_device(self): # Add an adapter. adapter_name = 'hci0' path = self.dbusmock_bluez.AddAdapter(adapter_name, 'my-computer') self.assertEqual(path, '/org/bluez/' + adapter_name) # Add a device. address = '11:22:33:44:55:66' alias = 'My Phone' path = self.dbusmock_bluez.AddDevice(adapter_name, address, alias) self.assertEqual(path, '/org/bluez/' + adapter_name + '/dev_' + address.replace(':', '_')) # Pair with the device. self.dbusmock_bluez.PairDevice(adapter_name, address, 5898764) # Check the device’s properties. out = '\n'.join(_run_bluetoothctl('info ' + address)) self.assertIn('Device ' + address, out) self.assertIn('Paired: yes', out) @unittest.skipUnless(have_pbap_client, 'pbap-client not installed (copy it from bluez/test)') class TestBlueZObex(dbusmock.DBusTestCase): '''Test mocking obexd''' @classmethod def setUpClass(cls): cls.start_session_bus() cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): # bluetoothd (self.p_mock, self.obj_bluez) = self.spawn_server_template( 'bluez5', {}, stdout=subprocess.PIPE) self.dbusmock_bluez = dbus.Interface(self.obj_bluez, 'org.bluez.Mock') # obexd (self.p_mock_obex, self.obj_obex) = self.spawn_server_template( 'bluez5-obex', {}, stdout=subprocess.PIPE) self.dbusmock = dbus.Interface(self.obj_obex, dbusmock.MOCK_IFACE) self.dbusmock_obex = dbus.Interface(self.obj_obex, 'org.bluez.obex.Mock') def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() self.p_mock_obex.stdout.close() self.p_mock_obex.terminate() self.p_mock_obex.wait() def test_everything(self): # Set up an adapter and device. adapter_name = 'hci0' device_address = '11:22:33:44:55:66' device_alias = 'My Phone' ml = GLib.MainLoop() self.dbusmock_bluez.AddAdapter(adapter_name, 'my-computer') self.dbusmock_bluez.AddDevice(adapter_name, device_address, device_alias) self.dbusmock_bluez.PairDevice(adapter_name, device_address) transferred_files = [] def _transfer_created_cb(path, params, transfer_filename): bus = self.get_dbus(False) obj = bus.get_object('org.bluez.obex', path) transfer = dbus.Interface(obj, 'org.bluez.obex.transfer1.Mock') with open(transfer_filename, 'wb') as f: f.write( b'BEGIN:VCARD\r\n' + b'VERSION:3.0\r\n' + b'FN:Forrest Gump\r\n' + b'TEL;TYPE=WORK,VOICE:(111) 555-1212\r\n' + b'TEL;TYPE=HOME,VOICE:(404) 555-1212\r\n' + b'EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com\r\n' + b'EMAIL:test@example.com\r\n' + b'URL;TYPE=HOME:http://example.com/\r\n' + b'URL:http://forest.com/\r\n' + b'URL:https://test.com/\r\n' + b'END:VCARD\r\n' ) transfer.UpdateStatus(True) transferred_files.append(transfer_filename) self.dbusmock_obex.connect_to_signal('TransferCreated', _transfer_created_cb) # Run pbap-client, then run the GLib main loop. The main loop will quit # after a timeout, at which point the code handles output from # pbap-client and waits for it to terminate. Integrating # process.communicate() with the GLib main loop to avoid the timeout is # too difficult. with subprocess.Popen(['pbap-client', device_address], stdout=subprocess.PIPE, stderr=sys.stderr, universal_newlines=True) as process: GLib.timeout_add(5000, ml.quit) ml.run() out = process.communicate()[0] lines = out.split('\n') lines = filter(lambda line: line != '', lines) lines = list(lines) # Clean up the transferred files. for f in transferred_files: try: os.remove(f) except OSError: pass # See what pbap-client sees. self.assertIn('Creating Session', lines) self.assertIn('--- Select Phonebook PB ---', lines) self.assertIn('--- GetSize ---', lines) self.assertIn('Size = 0', lines) self.assertIn('--- List vCard ---', lines) self.assertIn( 'Transfer /org/bluez/obex/client/session0/transfer0 complete', lines) self.assertIn( 'Transfer /org/bluez/obex/client/session0/transfer1 complete', lines) self.assertIn( 'Transfer /org/bluez/obex/client/session0/transfer2 complete', lines) self.assertIn( 'Transfer /org/bluez/obex/client/session0/transfer3 complete', lines) self.assertIn('FINISHED', lines) self.assertNotIn('ERROR', lines) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_cli.py0000644000175100001710000002121000000000000021051 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import importlib.util import os import unittest import shutil import sys import subprocess import tempfile import tracemalloc import dbus import dbusmock tracemalloc.start(25) have_upower = shutil.which('upower') class TestCLI(dbusmock.DBusTestCase): '''Test running dbusmock from the command line''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.start_session_bus() cls.system_con = cls.get_dbus(True) cls.session_con = cls.get_dbus() def setUp(self): self.p_mock = None def tearDown(self): if self.p_mock: if self.p_mock.stdout: self.p_mock.stdout.close() if self.p_mock.stderr: self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() self.p_mock = None def start_mock(self, args, wait_name, wait_path, wait_system=False): # pylint: disable=consider-using-with self.p_mock = subprocess.Popen([sys.executable, '-m', 'dbusmock'] + args, stdout=subprocess.PIPE, universal_newlines=True) self.wait_for_bus_object(wait_name, wait_path, wait_system) def test_session_bus(self): self.start_mock(['com.example.Test', '/', 'TestIface'], 'com.example.Test', '/') def test_system_bus(self): self.start_mock(['--system', 'com.example.Test', '/', 'TestIface'], 'com.example.Test', '/', True) def test_template_upower(self): self.start_mock(['-t', 'upower'], 'org.freedesktop.UPower', '/org/freedesktop/UPower', True) self.check_upower_running() def test_template_upower_explicit_path(self): spec = importlib.util.find_spec('dbusmock.templates.upower') self.assertTrue(os.path.exists(spec.origin)) self.start_mock(['-t', spec.origin], 'org.freedesktop.UPower', '/org/freedesktop/UPower', True) self.check_upower_running() def check_upower_running(self): # check that it actually ran the template, if we have upower if have_upower: out = subprocess.check_output(['upower', '--dump'], universal_newlines=True) self.assertRegex(out, r'on-battery:\s+no') mock_out = self.p_mock.stdout.readline() self.assertTrue('EnumerateDevices' in mock_out or 'GetAll' in mock_out, mock_out) def test_template_explicit_system(self): # --system is redundant here, but should not break self.start_mock(['--system', '-t', 'upower'], 'org.freedesktop.UPower', '/org/freedesktop/UPower', True) self.check_upower_running() def test_template_override_session(self): self.start_mock(['--session', '-t', 'upower'], 'org.freedesktop.UPower', '/org/freedesktop/UPower', False) def test_template_conflicting_bus(self): with self.assertRaises(subprocess.CalledProcessError) as cm: subprocess.check_output([sys.executable, '-m', 'dbusmock', '--system', '--session', '-t', 'upower'], stderr=subprocess.STDOUT, universal_newlines=True) err = cm.exception self.assertEqual(err.returncode, 2) self.assertRegex(err.output, '--system.*--session.*exclusive') def test_template_parameters(self): self.start_mock(['-t', 'upower', '-p', '{"DaemonVersion": "0.99.0", "OnBattery": true}'], 'org.freedesktop.UPower', '/org/freedesktop/UPower', True) # check that it actually ran the template, if we have upower if have_upower: out = subprocess.check_output(['upower', '--dump'], universal_newlines=True) self.assertRegex(out, r'daemon-version:\s+0\.99\.0') self.assertRegex(out, r'on-battery:\s+yes') def test_template_parameters_malformed_json(self): with self.assertRaises(subprocess.CalledProcessError) as cm: subprocess.check_output([sys.executable, '-m', 'dbusmock', '-t', 'upower', '-p', '{"DaemonVersion: "0.99.0"}'], stderr=subprocess.STDOUT, universal_newlines=True) err = cm.exception self.assertEqual(err.returncode, 2) self.assertRegex(err.output, 'Malformed JSON given for parameters:.* delimiter') def test_template_parameters_not_dict(self): with self.assertRaises(subprocess.CalledProcessError) as cm: subprocess.check_output([sys.executable, '-m', 'dbusmock', '-t', 'upower', '-p', '"banana"'], stderr=subprocess.STDOUT, universal_newlines=True) err = cm.exception self.assertEqual(err.returncode, 2) self.assertEqual(err.output, 'JSON parameters must be a dictionary\n') def test_template_local(self): with tempfile.NamedTemporaryFile(prefix='answer_', suffix='.py') as my_template: my_template.write(b'''import dbus BUS_NAME = 'universe.Ultimate' MAIN_OBJ = '/' MAIN_IFACE = 'universe.Ultimate' SYSTEM_BUS = False def load(mock, parameters): mock.AddMethods(MAIN_IFACE, [('Answer', '', 'i', 'ret = 42')]) ''') my_template.flush() # template specifies session bus self.start_mock(['-t', my_template.name], 'universe.Ultimate', '/', False) obj = self.session_con.get_object('universe.Ultimate', '/') if_u = dbus.Interface(obj, 'universe.Ultimate') self.assertEqual(if_u.Answer(), 42) def test_template_override_system(self): with tempfile.NamedTemporaryFile(prefix='answer_', suffix='.py') as my_template: my_template.write(b'''import dbus BUS_NAME = 'universe.Ultimate' MAIN_OBJ = '/' MAIN_IFACE = 'universe.Ultimate' SYSTEM_BUS = False def load(mock, parameters): mock.AddMethods(MAIN_IFACE, [('Answer', '', 'i', 'ret = 42')]) ''') my_template.flush() # template specifies session bus, but CLI overrides to system self.start_mock(['--system', '-t', my_template.name], 'universe.Ultimate', '/', True) obj = self.system_con.get_object('universe.Ultimate', '/') if_u = dbus.Interface(obj, 'universe.Ultimate') self.assertEqual(if_u.Answer(), 42) def test_object_manager(self): self.start_mock(['-m', 'com.example.Test', '/', 'TestIface'], 'com.example.Test', '/') obj = self.session_con.get_object('com.example.Test', '/') if_om = dbus.Interface(obj, dbusmock.OBJECT_MANAGER_IFACE) self.assertEqual(if_om.GetManagedObjects(), {}) # add a new object, should appear obj.AddObject('/a/b', 'org.Test', {'name': 'foo'}, dbus.Array([], signature='(ssss)')) self.assertEqual(if_om.GetManagedObjects(), {'/a/b': {'org.Test': {'name': 'foo'}}}) def test_no_args(self): with subprocess.Popen([sys.executable, '-m', 'dbusmock'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) as p: (out, err) = p.communicate() self.assertEqual(out, '') self.assertIn('must specify NAME', err) self.assertNotEqual(p.returncode, 0) def test_help(self): with subprocess.Popen([sys.executable, '-m', 'dbusmock', '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) as p: (out, err) = p.communicate() self.assertEqual(err, '') self.assertIn('INTERFACE', out) self.assertIn('--system', out) self.assertEqual(p.returncode, 0) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_code.py0000644000175100001710000000524200000000000021223 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import glob import shutil import subprocess import sys import unittest pycodestyle = shutil.which('pycodestyle-3') or shutil.which('pycodestyle') pyflakes = shutil.which('pyflakes-3') or shutil.which('pyflakes3') if subprocess.call(['python3', '-m', 'pylint', '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0: pylint = [sys.executable, '-m', 'pylint'] else: pylint = [] if subprocess.call(['python3', '-m', 'mypy', '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0: mypy = [sys.executable, '-m', 'mypy'] else: mypy = [] class StaticCodeTests(unittest.TestCase): @unittest.skipUnless(pyflakes, 'pyflakes3 not installed') def test_pyflakes(self): # pylint: disable=no-self-use subprocess.check_call([pyflakes, '.']) @unittest.skipUnless(pycodestyle, 'pycodestyle not installed') def test_codestyle(self): # pylint: disable=no-self-use subprocess.check_call([pycodestyle, '--max-line-length=130', '--ignore=E124,E402,E731,W504', '.']) @unittest.skipUnless(pylint, 'pylint not installed') def test_pylint(self): # pylint: disable=no-self-use subprocess.check_call(pylint + ['setup.py'] + glob.glob('dbusmock/*.py')) # signatures/arguments are not determined by us, docstrings are a bit pointless, and code repetition # is impractical to avoid (e.g. bluez4 and bluez5) subprocess.check_call(pylint + ['--score=n', '--disable=missing-function-docstring,R0801', '--disable=too-many-arguments,too-many-instance-attributes', 'dbusmock/templates/']) subprocess.check_call(pylint + ['--score=n', '--disable=missing-module-docstring,missing-class-docstring,missing-function-docstring', '--disable=too-many-public-methods,too-many-lines,R0801', 'tests/']) @unittest.skipUnless(mypy, 'mypy not installed') def test_types(self): # pylint: disable=no-self-use subprocess.check_call(mypy + ['setup.py', 'dbusmock/', 'tests/']) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_gnome_screensaver.py0000644000175100001710000000441600000000000024020 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2013 Canonical Ltd.' import unittest import os import sys import subprocess import fcntl import dbusmock class TestGnomeScreensaver(dbusmock.DBusTestCase): '''Test mocking gnome-screensaver''' @classmethod def setUpClass(cls): cls.start_session_bus() cls.dbus_con = cls.get_dbus(False) def setUp(self): (self.p_mock, self.obj_ss) = self.spawn_server_template( 'gnome_screensaver', {}, stdout=subprocess.PIPE) # set log to nonblocking flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_default_state(self): '''Not locked by default''' self.assertEqual(self.obj_ss.GetActive(), False) def test_lock(self): '''Lock()''' self.obj_ss.Lock() self.assertEqual(self.obj_ss.GetActive(), True) self.assertGreater(self.obj_ss.GetActiveTime(), 0) self.assertRegex(self.p_mock.stdout.read(), b'emit /org/gnome/ScreenSaver org.gnome.ScreenSaver.ActiveChanged True\n') def test_set_active(self): '''SetActive()''' self.obj_ss.SetActive(True) self.assertEqual(self.obj_ss.GetActive(), True) self.assertRegex(self.p_mock.stdout.read(), b'emit /org/gnome/ScreenSaver org.gnome.ScreenSaver.ActiveChanged True\n') self.obj_ss.SetActive(False) self.assertEqual(self.obj_ss.GetActive(), False) self.assertRegex(self.p_mock.stdout.read(), b'emit /org/gnome/ScreenSaver org.gnome.ScreenSaver.ActiveChanged False\n') if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_iio_sensors_proxy.py0000644000175100001710000004023700000000000024111 0ustar00runnerdocker00000000000000#!/usr/bin/python3 """ Tests for accounts service """ # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Marco Trevisan' __copyright__ = '(c) 2021 Canonical Ltd.' import fcntl import os import shutil import subprocess import sys import time import unittest import dbus import dbus.mainloop.glib from gi.repository import GLib import dbusmock dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) have_monitor_sensor = shutil.which('monitor-sensor') class TestIIOSensorsProxyBase(dbusmock.DBusTestCase): '''Test mocking iio-sensors-proxy''' dbus_interface = '' @classmethod def setUpClass(cls): super().setUpClass() cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): super().setUp() (self.p_mock, self.p_obj) = self.spawn_server_template( 'iio-sensors-proxy', {}, stdout=subprocess.PIPE) def tearDown(self): if self.p_mock: self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() super().tearDown() def get_property(self, name): return self.p_obj.Get(self.dbus_interface, name, dbus_interface=dbus.PROPERTIES_IFACE) def get_internal_property(self, name): return self.p_obj.GetInternalProperty(name) def set_internal_property(self, name, value): return self.p_obj.SetInternalProperty(self.dbus_interface, name, value) def wait_for_properties_changed(self, max_wait=2000): changed_properties = [] timeout_id = 0 def on_properties_changed(interface, properties, _invalidated): nonlocal changed_properties if interface == self.dbus_interface: changed_properties = properties.keys() def on_timeout(): nonlocal timeout_id timeout_id = 0 loop = GLib.MainLoop() timeout_id = GLib.timeout_add(max_wait, on_timeout) match = self.p_obj.connect_to_signal('PropertiesChanged', on_properties_changed, dbus.PROPERTIES_IFACE) while not changed_properties and timeout_id != 0: loop.get_context().iteration(True) if timeout_id: GLib.source_remove(timeout_id) match.remove() return changed_properties def wait_for_property_changed(self, property_name, expected_value): self.assertIn(property_name, self.wait_for_properties_changed()) self.assertEqual( self.get_internal_property(property_name), expected_value) class TestIIOSensorsProxy(TestIIOSensorsProxyBase): ''' main SensorsProxy interface tests ''' dbus_interface = 'net.hadess.SensorProxy' def test_accelerometer_none(self): self.assertFalse(self.get_property('HasAccelerometer')) def test_accelerometer_claimed(self): self.p_obj.ClaimAccelerometer() self.assertTrue(self.get_internal_property('AccelerometerOwners')) def test_accelerometer_claimed_released(self): self.p_obj.ClaimAccelerometer() self.assertTrue(self.get_internal_property('AccelerometerOwners')) self.p_obj.ReleaseAccelerometer() self.assertFalse(self.get_internal_property('AccelerometerOwners')) def test_accelerometer_available(self): self.assertFalse(self.get_property('HasAccelerometer')) self.set_internal_property('HasAccelerometer', True) self.assertTrue(self.get_property('HasAccelerometer')) def test_accelerometer_property_with_no_sensor(self): with self.assertRaises(dbus.exceptions.DBusException) as ctx: self.set_internal_property('AccelerometerOrientation', 'normal') self.assertEqual(ctx.exception.get_dbus_name(), 'org.freedesktop.DBus.Python.Exception') self.assertIn('Exception: No accelerometer sensor available', ctx.exception.get_dbus_message().split('\n')) def test_accelerometer_claimed_properties_changes(self): self.set_internal_property('HasAccelerometer', True) self.p_obj.ClaimAccelerometer() self.set_internal_property('AccelerometerOrientation', 'normal') self.wait_for_property_changed('AccelerometerOrientation', 'normal') def test_accelerometer_unclaimed_properties_changes(self): self.set_internal_property('HasAccelerometer', True) self.assertTrue(self.get_property('HasAccelerometer')) self.set_internal_property('AccelerometerOrientation', 'normal') self.assertFalse(self.wait_for_properties_changed(max_wait=500)) self.assertEqual(self.get_property('AccelerometerOrientation'), 'normal') def test_ambient_light_none(self): self.assertFalse(self.get_property('HasAmbientLight')) def test_ambient_light_claimed(self): self.p_obj.ClaimLight() self.assertTrue(self.get_internal_property('AmbientLightOwners')) self.assertFalse(self.get_property('HasAmbientLight')) def test_ambient_light_claimed_released(self): self.p_obj.ClaimLight() self.assertTrue(self.get_internal_property('AmbientLightOwners')) self.p_obj.ReleaseLight() self.assertFalse(self.get_internal_property('AmbientLightOwners')) def test_ambient_light_available(self): self.assertFalse(self.get_property('HasAmbientLight')) self.set_internal_property('HasAmbientLight', True) self.assertTrue(self.get_property('HasAmbientLight')) def test_ambient_light_property_with_no_sensor(self): with self.assertRaises(dbus.exceptions.DBusException) as ctx: self.set_internal_property('LightLevelUnit', 'vendor') self.assertEqual(ctx.exception.get_dbus_name(), 'org.freedesktop.DBus.Python.Exception') self.assertIn('Exception: No ambient_light sensor available', ctx.exception.get_dbus_message().split('\n')) with self.assertRaises(dbus.exceptions.DBusException) as ctx: self.set_internal_property('LightLevel', 0.5) self.assertEqual(ctx.exception.get_dbus_name(), 'org.freedesktop.DBus.Python.Exception') self.assertIn('Exception: No ambient_light sensor available', ctx.exception.get_dbus_message().split('\n')) def test_ambient_light_claimed_properties_changes(self): self.set_internal_property('HasAmbientLight', True) self.p_obj.ClaimLight() self.set_internal_property('LightLevelUnit', 'vendor') self.wait_for_property_changed('LightLevelUnit', 'vendor') self.set_internal_property('LightLevel', 111100.0) self.wait_for_property_changed('LightLevel', 111100.0) def test_ambient_light_unclaimed_properties_changes(self): self.set_internal_property('HasAmbientLight', True) self.assertTrue(self.get_property('HasAmbientLight')) self.set_internal_property('LightLevelUnit', 'vendor') self.assertFalse(self.wait_for_properties_changed(max_wait=500)) self.assertEqual(self.get_property('LightLevelUnit'), 'vendor') def test_proximity_none(self): self.assertFalse(self.get_property('HasProximity')) def test_proximity_claimed(self): self.p_obj.ClaimProximity() self.assertTrue(self.get_internal_property('ProximityOwners')) self.assertFalse(self.get_property('HasProximity')) def test_proximity_claimed_released(self): self.p_obj.ClaimProximity() self.assertTrue(self.get_internal_property('ProximityOwners')) self.assertFalse(self.get_property('HasProximity')) self.p_obj.ReleaseProximity() self.assertFalse(self.get_internal_property('ProximityOwners')) def test_proximity_available(self): self.assertFalse(self.get_property('HasProximity')) self.set_internal_property('HasProximity', True) self.assertTrue(self.get_property('HasProximity')) def test_proximity_property_with_no_sensor(self): with self.assertRaises(dbus.exceptions.DBusException) as ctx: self.set_internal_property('ProximityNear', True) self.assertEqual(ctx.exception.get_dbus_name(), 'org.freedesktop.DBus.Python.Exception') self.assertIn('Exception: No proximity sensor available', ctx.exception.get_dbus_message().split('\n')) def test_proximity_claimed_properties_changes(self): self.set_internal_property('HasProximity', True) self.p_obj.ClaimProximity() self.set_internal_property('ProximityNear', True) self.wait_for_property_changed('ProximityNear', True) def test_proximity_unclaimed_properties_changes(self): self.set_internal_property('HasProximity', True) self.assertTrue(self.get_property('HasProximity')) self.set_internal_property('ProximityNear', True) self.assertFalse(self.wait_for_properties_changed(max_wait=500)) self.assertTrue(self.get_property('ProximityNear')) class TestIIOSensorsProxyCompass(TestIIOSensorsProxyBase): ''' main SensorsProxy compass interface tests ''' dbus_interface = 'net.hadess.SensorProxy.Compass' def test_compass_none(self): self.assertFalse(self.get_property('HasCompass')) def test_compass_claimed(self): self.p_obj.ClaimCompass() self.assertTrue(self.get_internal_property('CompassOwners')) self.assertFalse(self.get_property('HasCompass')) def test_compass_claimed_released(self): self.p_obj.ClaimCompass() self.assertTrue(self.get_internal_property('CompassOwners')) self.assertFalse(self.get_property('HasCompass')) self.p_obj.ReleaseCompass() self.assertFalse(self.get_internal_property('CompassOwners')) def test_compass_available(self): self.assertFalse(self.get_property('HasCompass')) self.set_internal_property('HasCompass', True) self.assertTrue(self.get_property('HasCompass')) def test_compass_property_with_no_sensor(self): with self.assertRaises(dbus.exceptions.DBusException) as ctx: self.set_internal_property('CompassHeading', 180) self.assertEqual(ctx.exception.get_dbus_name(), 'org.freedesktop.DBus.Python.Exception') self.assertIn('Exception: No compass sensor available', ctx.exception.get_dbus_message().split('\n')) def test_compass_claimed_properties_changes(self): self.set_internal_property('HasCompass', True) self.p_obj.ClaimCompass() self.set_internal_property('CompassHeading', 55) self.wait_for_property_changed('CompassHeading', 55) def test_compass_unclaimed_properties_changes(self): self.set_internal_property('HasCompass', True) self.assertTrue(self.get_property('HasCompass')) self.set_internal_property('CompassHeading', 85) self.assertFalse(self.wait_for_properties_changed(max_wait=500)) self.assertEqual(self.get_property('CompassHeading'), 85) @unittest.skipUnless(have_monitor_sensor, 'monitor-sensor utility not available') class TestIIOSensorsProxyMonitorSensorBase(TestIIOSensorsProxyBase): ''' Base SensorsProxy interface tests using monitor-sensor''' p_monitor_sensor = None def start_monitor_sensor(self): self.assertIsNone(self.p_monitor_sensor) # pylint: disable=consider-using-with self.p_monitor_sensor = subprocess.Popen( 'monitor-sensor', stdout=subprocess.PIPE, stderr=subprocess.STDOUT) flags = fcntl.fcntl(self.p_monitor_sensor.stdout, fcntl.F_GETFL) fcntl.fcntl(self.p_monitor_sensor.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) self.assertOutputContains([ ' Waiting for iio-sensor-proxy to appear', '+++ iio-sensor-proxy appeared', ]) def stop_monitor_sensor(self): self.assertIsNotNone(self.p_monitor_sensor) self.assertEmptyOutput() self.p_monitor_sensor.stdout.close() self.p_monitor_sensor.terminate() self.p_monitor_sensor.wait() def tearDown(self): if self.p_monitor_sensor: self.stop_monitor_sensor() super().tearDown() def assertOutputContains(self, expected_lines, max_wait=2000): self.assertIsNotNone(self.p_monitor_sensor) start_time = int(time.time() * 1000) for line in expected_lines: output = None while True: output = self.p_monitor_sensor.stdout.readline() if output: break self.assertLessEqual(int(time.time() * 1000) - start_time, max_wait, msg='Timeout exceeded') self.assertEqual(output.decode('utf-8'), f'{line}\n') def assertOutputEquals(self, expected_lines, max_wait=2000): self.assertOutputContains(expected_lines, max_wait) self.assertEmptyOutput() def assertEmptyOutput(self, max_wait=100): start_time = int(time.time() * 1000) while int(time.time() * 1000) - start_time < max_wait: self.assertFalse(self.p_monitor_sensor.stdout.readline(), msg='Unexpected output') class TestIIOSensorsProxyMonitorSensor(TestIIOSensorsProxyMonitorSensorBase): ''' main SensorsProxy interface tests using monitor-sensor''' dbus_interface = 'net.hadess.SensorProxy' def test_accelerometer_added(self): self.set_internal_property('HasAccelerometer', True) self.start_monitor_sensor() self.assertOutputEquals([ '=== Has accelerometer (orientation: undefined)', '=== No ambient light sensor', '=== No proximity sensor', ]) def test_accelerometer_changes(self): self.test_accelerometer_added() self.set_internal_property('AccelerometerOrientation', 'normal') self.set_internal_property('AccelerometerOrientation', 'left-up') self.set_internal_property('AccelerometerOrientation', 'bottom-up') self.assertOutputEquals([ ' Accelerometer orientation changed: normal', ' Accelerometer orientation changed: left-up', ' Accelerometer orientation changed: bottom-up', ]) def test_ambient_light_added(self): self.set_internal_property('HasAmbientLight', True) self.start_monitor_sensor() self.assertOutputEquals([ '=== No accelerometer', '=== Has ambient light sensor (value: 0.000000, unit: lux)', '=== No proximity sensor', ]) def test_ambient_light_changes(self): self.test_ambient_light_added() self.set_internal_property('LightLevelUnit', 'vendor') self.set_internal_property('LightLevel', 0.3) self.set_internal_property('LightLevel', 0.5) self.set_internal_property('LightLevelUnit', 'lux') self.set_internal_property('LightLevel', 111100.0) self.assertOutputEquals([ ' Light changed: 0.300000 (vendor)', ' Light changed: 0.500000 (vendor)', ' Light changed: 111100.000000 (lux)', ]) def test_proximity_sensor_added(self): self.set_internal_property('HasProximity', True) self.start_monitor_sensor() self.assertOutputEquals([ '=== No accelerometer', '=== No ambient light sensor', '=== Has proximity sensor (near: 0)', ]) def test_proximity_sensor_changes(self): self.test_proximity_sensor_added() self.set_internal_property('ProximityNear', True) self.set_internal_property('ProximityNear', False) self.assertOutputEquals([ ' Proximity value changed: 1', ' Proximity value changed: 0', ]) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner( stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_logind.py0000644000175100001710000001321500000000000021564 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2013 Canonical Ltd.' import os import re import shutil import subprocess import sys import unittest import tracemalloc import dbus import dbusmock tracemalloc.start(25) have_loginctl = shutil.which('loginctl') @unittest.skipUnless(have_loginctl, 'loginctl not installed') @unittest.skipUnless(os.path.exists('/run/systemd/system'), '/run/systemd/system does not exist') class TestLogind(dbusmock.DBusTestCase): '''Test mocking logind''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) if have_loginctl: out = subprocess.check_output(['loginctl', '--version'], universal_newlines=True) cls.version = re.search(r'(\d+)', out.splitlines()[0]).group(1) def setUp(self): self.p_mock = None def tearDown(self): if self.p_mock: self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_empty(self): (self.p_mock, _) = self.spawn_server_template('logind', {}, stdout=subprocess.PIPE) cmd = ['loginctl'] if self.version >= '209': cmd.append('--no-legend') out = subprocess.check_output(cmd + ['list-sessions'], universal_newlines=True) self.assertEqual(out, '') out = subprocess.check_output(cmd + ['list-seats'], universal_newlines=True) self.assertEqual(out, '') out = subprocess.check_output(cmd + ['list-users'], universal_newlines=True) self.assertEqual(out, '') def test_session(self): (self.p_mock, obj_logind) = self.spawn_server_template('logind', {}, stdout=subprocess.PIPE) obj_logind.AddSession('c1', 'seat0', 500, 'joe', True) out = subprocess.check_output(['loginctl', 'list-seats'], universal_newlines=True) self.assertRegex(out, r'(^|\n)seat0\s+') out = subprocess.check_output(['loginctl', 'show-seat', 'seat0'], universal_newlines=True) self.assertRegex(out, 'Id=seat0') if self.version <= '208': self.assertRegex(out, 'ActiveSession=c1') self.assertRegex(out, 'Sessions=c1') out = subprocess.check_output(['loginctl', 'list-users'], universal_newlines=True) self.assertRegex(out, r'(^|\n)\s*500\s+joe\s*($|\n)') # note, this does an actual getpwnam() in the client, so we cannot call # this with hardcoded user names; get from actual user in the system # out = subprocess.check_output(['loginctl', 'show-user', 'joe'], # universal_newlines=True) # self.assertRegex(out, 'UID=500') # self.assertRegex(out, 'GID=500') # self.assertRegex(out, 'Name=joe') # self.assertRegex(out, 'Sessions=c1') # self.assertRegex(out, 'State=active') out = subprocess.check_output(['loginctl', 'list-sessions'], universal_newlines=True) self.assertRegex(out, 'c1 +500 +joe +seat0') out = subprocess.check_output(['loginctl', 'show-session', 'c1'], universal_newlines=True) self.assertRegex(out, 'Id=c1') self.assertRegex(out, 'Class=user') self.assertRegex(out, 'Active=yes') self.assertRegex(out, 'State=active') self.assertRegex(out, 'Name=joe') self.assertRegex(out, 'LockedHint=no') session_mock = dbus.Interface(self.dbus_con.get_object( 'org.freedesktop.login1', '/org/freedesktop/login1/session/c1'), 'org.freedesktop.login1.Session') session_mock.SetLockedHint(True) out = subprocess.check_output(['loginctl', 'show-session', 'c1'], universal_newlines=True) self.assertRegex(out, 'Id=c1') self.assertRegex(out, 'LockedHint=yes') def test_properties(self): (self.p_mock, obj_logind) = self.spawn_server_template('logind', {}, stdout=subprocess.PIPE) props = obj_logind.GetAll('org.freedesktop.login1.Manager', interface='org.freedesktop.DBus.Properties') self.assertEqual(props['PreparingForSleep'], False) self.assertEqual(props['IdleSinceHint'], 0) def test_inhibit(self): (self.p_mock, obj_logind) = self.spawn_server_template('logind', {}, stdout=subprocess.PIPE) # what, who, why, mode fd = obj_logind.Inhibit('suspend', 'testcode', 'purpose', 'delay') # Our inhibitor is held out = subprocess.check_output(['systemd-inhibit'], universal_newlines=True) self.assertRegex(out, 'testcode +[0-9]+ +[^ ]* +[0-9]+ +[^ ]* +suspend purpose delay') del fd # No inhibitor is held out = subprocess.check_output(['systemd-inhibit'], universal_newlines=True) self.assertRegex(out, 'No inhibitors') if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_low_memory_monitor.py0000644000175100001710000000353500000000000024254 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Bastien Nocera' __copyright__ = '(c) 2019 Red Hat Inc.' import unittest import sys import subprocess import fcntl import os import dbus import dbus.mainloop.glib import dbusmock dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) class TestLowMemoryMonitor(dbusmock.DBusTestCase): '''Test mocking low-memory-monitor''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): (self.p_mock, self.obj_lmm) = self.spawn_server_template( 'low_memory_monitor', {}, stdout=subprocess.PIPE) # set log to nonblocking flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) self.last_warning = -1 self.dbusmock = dbus.Interface(self.obj_lmm, dbusmock.MOCK_IFACE) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_low_memory_warning_signal(self): '''LowMemoryWarning signal''' self.dbusmock.EmitWarning(100) log = self.p_mock.stdout.read() self.assertRegex(log, b'[0-9.]+ emit .*LowMemoryWarning 100\n') self.dbusmock.EmitWarning(255) log = self.p_mock.stdout.read() self.assertRegex(log, b'[0-9.]+ emit .*LowMemoryWarning 255\n') if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_networkmanager.py0000644000175100001710000006041000000000000023333 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Iftikhar Ahmad' __copyright__ = '(c) 2012 Canonical Ltd.' import os import re import shutil import subprocess import sys import unittest import tracemalloc from gi.repository import GLib import dbus import dbus.mainloop.glib import dbusmock from dbusmock.templates.networkmanager import DeviceState from dbusmock.templates.networkmanager import NM80211ApSecurityFlags from dbusmock.templates.networkmanager import InfrastructureMode from dbusmock.templates.networkmanager import NMActiveConnectionState from dbusmock.templates.networkmanager import NMState from dbusmock.templates.networkmanager import NMConnectivityState from dbusmock.templates.networkmanager import (CSETTINGS_IFACE, MANAGER_IFACE, SETTINGS_OBJ, SETTINGS_IFACE) tracemalloc.start(25) dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) have_nmcli = shutil.which('nmcli') @unittest.skipUnless(have_nmcli, 'nmcli not installed') class TestNetworkManager(dbusmock.DBusTestCase): '''Test mocking NetworkManager''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) os.environ['G_DEBUG'] = 'fatal-warnings,fatal-criticals' # prepare environment which avoids translations cls.lang_env = os.environ.copy() try: del cls.lang_env['LANG'] except KeyError: pass try: del cls.lang_env['LANGUAGE'] except KeyError: pass cls.lang_env['LC_MESSAGES'] = 'C' def setUp(self): (self.p_mock, self.obj_networkmanager) = self.spawn_server_template( 'networkmanager', {'NetworkingEnabled': True, 'WwanEnabled': False}, stdout=subprocess.PIPE) self.dbusmock = dbus.Interface(self.obj_networkmanager, dbusmock.MOCK_IFACE) self.settings = dbus.Interface( self.dbus_con.get_object(MANAGER_IFACE, SETTINGS_OBJ), SETTINGS_IFACE) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def read_general(self): return subprocess.check_output(['nmcli', '--nocheck', 'general'], env=self.lang_env, universal_newlines=True) def read_networking(self): return subprocess.check_output(['nmcli', '--nocheck', 'networking'], env=self.lang_env, universal_newlines=True) def read_connection(self): return subprocess.check_output(['nmcli', '--nocheck', 'connection'], env=self.lang_env, universal_newlines=True) def read_active_connection(self): return subprocess.check_output(['nmcli', '--nocheck', 'connection', 'show', '--active'], env=self.lang_env, universal_newlines=True) def read_device(self): return subprocess.check_output(['nmcli', '--nocheck', 'dev'], env=self.lang_env, universal_newlines=True) def read_device_wifi(self): return subprocess.check_output(['nmcli', '--nocheck', 'dev', 'wifi', 'list', '--rescan', 'no'], env=self.lang_env, universal_newlines=True) def test_one_eth_disconnected(self): self.dbusmock.AddEthernetDevice('mock_Ethernet1', 'eth0', DeviceState.DISCONNECTED) out = self.read_device() self.assertRegex(out, r'eth0.*\sdisconnected') def test_one_eth_connected(self): self.dbusmock.AddEthernetDevice('mock_Ethernet1', 'eth0', DeviceState.ACTIVATED) out = self.read_device() self.assertRegex(out, r'eth0.*\sconnected') def test_two_eth(self): # test with numeric state value self.dbusmock.AddEthernetDevice('mock_Ethernet1', 'eth0', 30) self.dbusmock.AddEthernetDevice('mock_Ethernet2', 'eth1', DeviceState.ACTIVATED) out = self.read_device() self.assertRegex(out, r'eth0.*\sdisconnected') self.assertRegex(out, r'eth1.*\sconnected') def test_wifi_without_access_points(self): self.dbusmock.AddWiFiDevice('mock_WiFi1', 'wlan0', DeviceState.ACTIVATED) out = self.read_device() self.assertRegex(out, r'wlan0.*\sconnected') def test_eth_and_wifi(self): self.dbusmock.AddEthernetDevice('mock_Ethernet1', 'eth0', DeviceState.DISCONNECTED) self.dbusmock.AddWiFiDevice('mock_WiFi1', 'wlan0', DeviceState.ACTIVATED) out = self.read_device() self.assertRegex(out, r'eth0.*\sdisconnected') self.assertRegex(out, r'wlan0.*\sconnected') def test_one_wifi_with_accesspoints(self): wifi = self.dbusmock.AddWiFiDevice('mock_WiFi2', 'wlan0', DeviceState.ACTIVATED) self.dbusmock.AddAccessPoint(wifi, 'Mock_AP1', 'AP_1', '00:23:F8:7E:12:BB', InfrastructureMode.NM_802_11_MODE_ADHOC, 2425, 5400, 82, NM80211ApSecurityFlags.NM_802_11_AP_SEC_NONE) self.dbusmock.AddAccessPoint(wifi, 'Mock_AP3', 'AP_3', '00:23:F8:7E:12:BC', InfrastructureMode.NM_802_11_MODE_INFRA, 2425, 5400, 82, NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_PSK) out = self.read_device() aps = self.read_device_wifi() self.assertRegex(out, r'wlan0.*\sconnected') self.assertRegex(aps, r'AP_1.*\sAd-Hoc') self.assertRegex(aps, r'AP_3.*\sInfra') # connect to non-existing wifi res = subprocess.run(['nmcli', 'dev', 'wifi', 'connect', 'nonexisting'], check=False, capture_output=True) self.assertNotEqual(res.returncode, 0) self.assertRegex(res.stderr, b'No network.*nonexisting') self.assertRegex(self.read_device(), r'wlan0.*\sconnected\s+--') # connect to existing wifi with password subprocess.check_call(['nmcli', 'dev', 'wifi', 'connect', 'AP_3', 'password', 's3kr1t']) self.assertRegex(self.read_device(), r'wlan0.*\sconnected\s+AP_3') # connect to existing wifi without password subprocess.check_call(['nmcli', 'dev', 'wifi', 'connect', 'AP_1']) self.assertRegex(self.read_device(), r'wlan0.*\sconnected\s+AP_1') def test_two_wifi_with_accesspoints(self): wifi1 = self.dbusmock.AddWiFiDevice('mock_WiFi1', 'wlan0', DeviceState.ACTIVATED) wifi2 = self.dbusmock.AddWiFiDevice('mock_WiFi2', 'wlan1', DeviceState.UNAVAILABLE) self.dbusmock.AddAccessPoint(wifi1, 'Mock_AP0', 'AP_0', '00:23:F8:7E:12:BA', InfrastructureMode.NM_802_11_MODE_UNKNOWN, 2425, 5400, 82, 0x400) self.dbusmock.AddAccessPoint(wifi2, 'Mock_AP1', 'AP_1', '00:23:F8:7E:12:BB', InfrastructureMode.NM_802_11_MODE_ADHOC, 2425, 5400, 82, NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_PSK) self.dbusmock.AddAccessPoint(wifi2, 'Mock_AP3', 'AP_2', '00:23:F8:7E:12:BC', InfrastructureMode.NM_802_11_MODE_INFRA, 2425, 5400, 82, 0x400) out = self.read_device() aps = self.read_device_wifi() self.assertRegex(out, r'wlan0.*\sconnected') self.assertRegex(out, r'wlan1.*\sunavailable') self.assertRegex(aps, r'AP_0.*\s(Unknown|N/A)') self.assertRegex(aps, r'AP_1.*\sAd-Hoc') self.assertRegex(aps, r'AP_2.*\sInfra') def test_wifi_with_connection(self): wifi1 = self.dbusmock.AddWiFiDevice('mock_WiFi1', 'wlan0', DeviceState.ACTIVATED) ap1 = self.dbusmock.AddAccessPoint( wifi1, 'Mock_AP1', 'The_SSID', '00:23:F8:7E:12:BB', InfrastructureMode.NM_802_11_MODE_ADHOC, 2425, 5400, 82, NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_PSK) con1 = self.dbusmock.AddWiFiConnection(wifi1, 'Mock_Con1', 'The_SSID', 'wpa-psk') self.assertRegex(self.read_connection(), r'The_SSID.*\s(802-11-wireless|wifi)') self.assertEqual(ap1, '/org/freedesktop/NetworkManager/AccessPoint/Mock_AP1') self.assertEqual(con1, '/org/freedesktop/NetworkManager/Settings/Mock_Con1') def test_global_state(self): self.dbusmock.SetGlobalConnectionState(NMState.NM_STATE_CONNECTED_GLOBAL) self.assertRegex(self.read_general(), r'connected.*\sfull') self.dbusmock.SetGlobalConnectionState(NMState.NM_STATE_CONNECTED_SITE) self.assertRegex(self.read_general(), r'connected \(site only\).*\sfull') self.dbusmock.SetGlobalConnectionState(NMState.NM_STATE_CONNECTED_LOCAL) self.assertRegex(self.read_general(), r'connected \(local only\).*\sfull') self.dbusmock.SetGlobalConnectionState(NMState.NM_STATE_CONNECTING) self.assertRegex(self.read_general(), r'connecting.*\sfull') self.dbusmock.SetGlobalConnectionState(NMState.NM_STATE_DISCONNECTING) self.assertRegex(self.read_general(), r'disconnecting.*\sfull') self.dbusmock.SetGlobalConnectionState(NMState.NM_STATE_DISCONNECTED) self.assertRegex(self.read_general(), r'disconnected.*\sfull') self.dbusmock.SetGlobalConnectionState(NMState.NM_STATE_ASLEEP) self.assertRegex(self.read_general(), r'asleep.*\sfull') def test_connectivity_state(self): self.dbusmock.SetConnectivity(NMConnectivityState.NM_CONNECTIVITY_FULL) self.assertRegex(self.read_general(), r'connected.*\sfull') self.dbusmock.SetConnectivity(NMConnectivityState.NM_CONNECTIVITY_LIMITED) self.assertRegex(self.read_general(), r'connected.*\slimited') self.dbusmock.SetConnectivity(NMConnectivityState.NM_CONNECTIVITY_PORTAL) self.assertRegex(self.read_general(), r'connected.*\sportal') self.dbusmock.SetConnectivity(NMConnectivityState.NM_CONNECTIVITY_NONE) self.assertRegex(self.read_general(), r'connected.*\snone') def test_networking(self): self.dbusmock.SetNetworkingEnabled(False) self.assertRegex(self.read_networking(), 'disabled') self.dbusmock.SetNetworkingEnabled(True) self.assertRegex(self.read_networking(), 'enabled') def test_wifi_with_active_connection(self): wifi1 = self.dbusmock.AddWiFiDevice('mock_WiFi1', 'wlan0', DeviceState.ACTIVATED) ap1 = self.dbusmock.AddAccessPoint( wifi1, 'Mock_AP1', 'The_SSID', '00:23:F8:7E:12:BB', InfrastructureMode.NM_802_11_MODE_INFRA, 2425, 5400, 82, NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_PSK) con1 = self.dbusmock.AddWiFiConnection(wifi1, 'Mock_Con1', 'The_SSID', '') active_con1 = self.dbusmock.AddActiveConnection( [wifi1], con1, ap1, 'Mock_Active1', NMActiveConnectionState.NM_ACTIVE_CONNECTION_STATE_ACTIVATED) self.assertEqual(ap1, '/org/freedesktop/NetworkManager/AccessPoint/Mock_AP1') self.assertEqual(con1, '/org/freedesktop/NetworkManager/Settings/Mock_Con1') self.assertEqual(active_con1, '/org/freedesktop/NetworkManager/ActiveConnection/Mock_Active1') self.assertRegex(self.read_general(), r'connected.*\sfull') self.assertRegex(self.read_connection(), r'The_SSID.*\s(802-11-wireless|wifi)') self.assertRegex(self.read_active_connection(), r'The_SSID.*\s(802-11-wireless|wifi)') self.assertRegex(self.read_device_wifi(), 'The_SSID') self.dbusmock.RemoveActiveConnection(wifi1, active_con1) self.assertRegex(self.read_connection(), r'The_SSID.*\s(802-11-wireless|wifi)') self.assertFalse(re.compile(r'The_SSID.*\s(802-11-wireless|wifi)').search(self.read_active_connection())) self.assertRegex(self.read_device_wifi(), 'The_SSID') self.dbusmock.RemoveWifiConnection(wifi1, con1) self.assertFalse(re.compile(r'The_SSID.*\s(802-11-wireless|wifi)').search(self.read_connection())) self.assertRegex(self.read_device_wifi(), 'The_SSID') self.dbusmock.RemoveAccessPoint(wifi1, ap1) self.assertFalse(re.compile('The_SSID').search(self.read_device_wifi())) def test_add_connection(self): self.dbusmock.AddWiFiDevice('mock_WiFi1', 'wlan0', DeviceState.ACTIVATED) uuid = '11111111-1111-1111-1111-111111111111' settings = dbus.Dictionary({ 'connection': dbus.Dictionary({ 'id': 'test connection', 'uuid': uuid, 'type': '802-11-wireless'}, signature='sv'), '802-11-wireless': dbus.Dictionary({ 'ssid': dbus.ByteArray('The_SSID'.encode('UTF-8'))}, signature='sv') }, signature='sa{sv}') con1 = self.settings.AddConnection(settings) self.assertEqual(con1, '/org/freedesktop/NetworkManager/Settings/0') self.assertRegex(self.read_connection(), uuid + r'.*\s(802-11-wireless|wifi)') # Use the same settings, but this one will autoconnect. uuid2 = '22222222-2222-2222-2222-222222222222' settings['connection']['autoconnect'] = dbus.Boolean( True, variant_level=1) settings['connection']['uuid'] = uuid2 con2 = self.settings.AddConnection(settings) self.assertEqual(con2, '/org/freedesktop/NetworkManager/Settings/1') self.assertRegex(self.read_general(), r'connected.*\sfull') self.assertRegex(self.read_connection(), uuid2 + r'.*\s(802-11-wireless|wifi)') self.assertRegex(self.read_active_connection(), uuid2 + r'.*\s(802-11-wireless|wifi)') def test_update_connection(self): uuid = '133d8eb9-6de6-444f-8b37-f40bf9e33226' settings = dbus.Dictionary({ 'connection': dbus.Dictionary({ 'id': 'test wireless', 'uuid': uuid, 'type': '802-11-wireless'}, signature='sv'), '802-11-wireless': dbus.Dictionary({ 'ssid': dbus.ByteArray('The_SSID'.encode('UTF-8'))}, signature='sv') }, signature='sa{sv}') con1 = self.settings.AddConnection(settings) con1_iface = dbus.Interface( self.dbus_con.get_object(MANAGER_IFACE, con1), CSETTINGS_IFACE) self.assertEqual(con1, '/org/freedesktop/NetworkManager/Settings/0') self.assertRegex(self.read_connection(), uuid + r'.*\s(802-11-wireless|wifi)') new_settings = dbus.Dictionary({ 'connection': dbus.Dictionary({ 'id': 'test wired', 'type': '802-3-ethernet'}, signature='sv'), '802-3-ethernet': dbus.Dictionary({ 'name': '802-3-ethernet' }, signature='sv')}, signature='sa{sv}') con1_iface.Update(new_settings) self.assertRegex(self.read_connection(), uuid + r'.*\s(ethernet|802-3-ethernet)') def test_remove_connection(self): wifi1 = self.dbusmock.AddWiFiDevice('mock_WiFi1', 'wlan0', DeviceState.ACTIVATED) ap1 = self.dbusmock.AddAccessPoint( wifi1, 'Mock_AP1', 'The_SSID', '00:23:F8:7E:12:BB', InfrastructureMode.NM_802_11_MODE_INFRA, 2425, 5400, 82, NM80211ApSecurityFlags.NM_802_11_AP_SEC_KEY_MGMT_PSK) con1 = self.dbusmock.AddWiFiConnection(wifi1, 'Mock_Con1', 'The_SSID', '') self.dbusmock.AddActiveConnection( [wifi1], con1, ap1, 'Mock_Active1', NMActiveConnectionState.NM_ACTIVE_CONNECTION_STATE_ACTIVATED) con1_i = dbus.Interface( self.dbus_con.get_object(MANAGER_IFACE, con1), CSETTINGS_IFACE) con1_i.Delete() self.assertRegex(self.read_general(), r'disconnected.*\sfull') self.assertFalse(re.compile(r'The_SSID.*\s802-11-wireless').search(self.read_active_connection())) self.assertRegex(self.read_device(), r'wlan0.*\sdisconnected') def test_add_remove_settings(self): connection = { 'connection': { 'timestamp': 1441979296, 'type': 'vpn', 'id': 'a', 'uuid': '11111111-1111-1111-1111-111111111111' }, 'vpn': { 'service-type': 'org.freedesktop.NetworkManager.openvpn', 'data': { 'connection-type': 'tls' } }, 'ipv4': { 'routes': dbus.Array([], signature='o'), 'never-default': True, 'addresses': dbus.Array([], signature='o'), 'dns': dbus.Array([], signature='o'), 'method': 'auto' }, 'ipv6': { 'addresses': dbus.Array([], signature='o'), 'ip6-privacy': 0, 'dns': dbus.Array([], signature='o'), 'never-default': True, 'routes': dbus.Array([], signature='o'), 'method': 'auto' } } connectionA = self.settings.AddConnection(connection) connection['connection']['id'] = 'b' connection['connection']['uuid'] = '11111111-1111-1111-1111-111111111112' connectionB = self.settings.AddConnection(connection) self.assertEqual(self.settings.ListConnections(), [connectionA, connectionB]) connectionA_i = dbus.Interface( self.dbus_con.get_object(MANAGER_IFACE, connectionA), CSETTINGS_IFACE) connectionA_i.Delete() self.assertEqual(self.settings.ListConnections(), [connectionB]) connection['connection']['id'] = 'c' connection['connection']['uuid'] = '11111111-1111-1111-1111-111111111113' connectionC = self.settings.AddConnection(connection) self.assertEqual(self.settings.ListConnections(), [connectionB, connectionC]) def test_add_update_settings(self): connection = { 'connection': { 'timestamp': 1441979296, 'type': 'vpn', 'id': 'a', 'uuid': '11111111-1111-1111-1111-111111111111' }, 'vpn': { 'service-type': 'org.freedesktop.NetworkManager.openvpn', 'data': dbus.Dictionary({ 'connection-type': 'tls' }, signature='ss') }, 'ipv4': { 'routes': dbus.Array([], signature='o'), 'never-default': True, 'addresses': dbus.Array([], signature='o'), 'dns': dbus.Array([], signature='o'), 'method': 'auto' }, 'ipv6': { 'addresses': dbus.Array([], signature='o'), 'ip6-privacy': 0, 'dns': dbus.Array([], signature='o'), 'never-default': True, 'routes': dbus.Array([], signature='o'), 'method': 'auto' } } connectionA = self.settings.AddConnection(connection) self.assertEqual(self.settings.ListConnections(), [connectionA]) connectionA_i = dbus.Interface( self.dbus_con.get_object(MANAGER_IFACE, connectionA), CSETTINGS_IFACE) connection['connection']['id'] = 'b' def do_update(): connectionA_i.Update(connection) caught = [] ml = GLib.MainLoop() def catch(*_, **kwargs): if (kwargs['interface'] == 'org.freedesktop.NetworkManager.Settings.Connection' and kwargs['member'] == 'Updated'): caught.append(kwargs['path']) ml.quit() self.dbus_con.add_signal_receiver(catch, interface_keyword='interface', path_keyword='path', member_keyword='member') GLib.timeout_add(200, do_update) # ensure that the loop quits even when we don't catch anything GLib.timeout_add(3000, ml.quit) ml.run() self.assertEqual(connectionA_i.GetSettings(), connection) self.assertEqual(caught, [connectionA]) def test_settings_secrets(self): secrets = dbus.Dictionary({ 'cert-pass': 'certificate password', 'password': 'the password', }, signature='ss') connection = { 'connection': { 'timestamp': 1441979296, 'type': 'vpn', 'id': 'a', 'uuid': '11111111-1111-1111-1111-111111111111' }, 'vpn': { 'service-type': 'org.freedesktop.NetworkManager.openvpn', 'data': dbus.Dictionary({ 'connection-type': 'password-tls', 'remote': 'remotey', 'ca': '/my/ca.crt', 'cert': '/my/cert.crt', 'cert-pass-flags': '1', 'key': '/my/key.key', 'password-flags': "1", }, signature='ss'), 'secrets': secrets }, 'ipv4': { 'routes': dbus.Array([], signature='o'), 'never-default': True, 'addresses': dbus.Array([], signature='o'), 'dns': dbus.Array([], signature='o'), 'method': 'auto' }, 'ipv6': { 'addresses': dbus.Array([], signature='o'), 'ip6-privacy': 0, 'dns': dbus.Array([], signature='o'), 'never-default': True, 'routes': dbus.Array([], signature='o'), 'method': 'auto' } } connectionPath = self.settings.AddConnection(connection) self.assertEqual(self.settings.ListConnections(), [connectionPath]) connection_i = dbus.Interface( self.dbus_con.get_object(MANAGER_IFACE, connectionPath), CSETTINGS_IFACE) # We expect there to be no secrets in the normal settings dict del connection['vpn']['secrets'] self.assertEqual(connection_i.GetSettings(), connection) # Secrets request should contain just vpn section with the secrets in self.assertEqual(connection_i.GetSecrets('vpn'), {'vpn': {'secrets': secrets}}) def test_get_conn_by_uuid(self): uuid = '133d8eb9-6de6-444f-8b37-f40bf9e33226' settings = dbus.Dictionary({ 'connection': dbus.Dictionary({ 'id': 'test wireless', 'uuid': uuid, 'type': '802-11-wireless'}, signature='sv'), '802-11-wireless': dbus.Dictionary({ 'ssid': dbus.ByteArray('The_SSID'.encode('UTF-8'))}, signature='sv') }, signature='sa{sv}') connectionPath = self.settings.AddConnection(settings) self.assertEqual(self.settings.GetConnectionByUuid(uuid), connectionPath) fakeuuid = '123123123213213' with self.assertRaisesRegex(dbus.exceptions.DBusException, f".*uuid.*{fakeuuid}$"): self.settings.GetConnectionByUuid(fakeuuid) if __name__ == '__main__': unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_notification_daemon.py0000644000175100001710000001051300000000000024317 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import unittest import sys import subprocess import fcntl import os import dbus import dbusmock try: notify_send_version = subprocess.check_output(['notify-send', '--version'], universal_newlines=True) notify_send_version = notify_send_version.split()[-1] except (OSError, subprocess.CalledProcessError): notify_send_version = '' @unittest.skipUnless(notify_send_version, 'notify-send not installed') class TestNotificationDaemon(dbusmock.DBusTestCase): '''Test mocking notification-daemon''' @classmethod def setUpClass(cls): cls.start_session_bus() cls.dbus_con = cls.get_dbus(False) def setUp(self): (self.p_mock, self.obj_daemon) = self.spawn_server_template( 'notification_daemon', {}, stdout=subprocess.PIPE) # set log to nonblocking flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_no_options(self): '''notify-send with no options''' subprocess.check_call(['notify-send', 'title', 'my text']) log = self.p_mock.stdout.read() self.assertRegex(log, b'[0-9.]+ Notify "notify-send" 0 "" "title" "my text" \\[\\]') @unittest.skipIf(notify_send_version < '0.7.5', 'this requires libnotify >= 0.7.5') def test_options(self): '''notify-send with some options''' subprocess.check_call(['notify-send', '-t', '27', '-a', 'fooApp', '-i', 'warning_icon', 'title', 'my text']) log = self.p_mock.stdout.read() # HACK: Why is the timeout missing on s390x? if os.uname().machine == 's390x': self.assertRegex(log, b'[0-9.]+ Notify "fooApp" 0 "warning_icon" "title" "my text" \\[\\] {"urgency": 1}') else: self.assertRegex(log, b'[0-9.]+ Notify "fooApp" 0 "warning_icon" "title" "my text" \\[\\] {"urgency": 1} 27\n') def test_id(self): '''ID handling''' notify_proxy = dbus.Interface( self.dbus_con.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications'), 'org.freedesktop.Notifications') # with input ID 0 it should generate new IDs id_ = notify_proxy.Notify('test', 0, '', 'summary', 'body', [], {}, -1) self.assertEqual(id_, 1) id_ = notify_proxy.Notify('test', 0, '', 'summary', 'body', [], {}, -1) self.assertEqual(id_, 2) # an existing ID should just be bounced back id_ = notify_proxy.Notify('test', 4, '', 'summary', 'body', [], {}, -1) self.assertEqual(id_, 4) id_ = notify_proxy.Notify('test', 1, '', 'summary', 'body', [], {}, -1) self.assertEqual(id_, 1) # the previous doesn't forget the counter id_ = notify_proxy.Notify('test', 0, '', 'summary', 'body', [], {}, -1) self.assertEqual(id_, 3) def test_close(self): '''CloseNotification() and NotificationClosed() signal''' notify_proxy = dbus.Interface( self.dbus_con.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications'), 'org.freedesktop.Notifications') id_ = notify_proxy.Notify('test', 0, '', 'summary', 'body', [], {}, -1) self.assertEqual(id_, 1) # known notification, should send a signal notify_proxy.CloseNotification(id_) log = self.p_mock.stdout.read() self.assertRegex(log, b'[0-9.]+ emit .*NotificationClosed 1 1\n') # unknown notification, don't send a signal notify_proxy.CloseNotification(id_ + 1) log = self.p_mock.stdout.read() self.assertNotIn(b'NotificationClosed', log) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_ofono.py0000644000175100001710000001524300000000000021433 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2013 Canonical Ltd.' import unittest import sys import subprocess import os import dbus import dbusmock script_dir = os.environ.get('OFONO_SCRIPT_DIR', '/usr/share/ofono/scripts') have_scripts = os.access(os.path.join(script_dir, 'list-modems'), os.X_OK) @unittest.skipUnless(have_scripts, 'ofono scripts not available, set $OFONO_SCRIPT_DIR') class TestOfono(dbusmock.DBusTestCase): '''Test mocking ofonod''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) (cls.p_mock, cls.obj_ofono) = cls.spawn_server_template( 'ofono', {}, stdout=subprocess.PIPE) def setUp(self): self.obj_ofono.Reset() def test_list_modems(self): '''Manager.GetModems()''' out = subprocess.check_output([os.path.join(script_dir, 'list-modems')]) self.assertTrue(out.startswith(b'[ /ril_0 ]'), out) self.assertIn(b'Powered = 1', out) self.assertIn(b'Online = 1', out) self.assertIn(b'Model = Mock Modem', out) self.assertIn(b'[ org.ofono.NetworkRegistration ]', out) self.assertIn(b'Status = registered', out) self.assertIn(b'Name = fake.tel', out) self.assertIn(b'Technology = gsm', out) self.assertIn(b'[ org.ofono.SimManager ]', out) self.assertIn(b'PinRequired = none', out) self.assertIn(b'Present = 1', out) self.assertIn(b'CardIdentifier = 893581234000000000000', out) self.assertIn(b'MobileCountryCode = 310', out) self.assertIn(b'MobileNetworkCode = 150', out) self.assertIn(b'Serial = 12345678-1234-1234-1234-000000000000', out) self.assertIn(b'SubscriberIdentity = 310150000000000', out) def test_outgoing_call(self): '''outgoing voice call''' # no calls by default out = subprocess.check_output([os.path.join(script_dir, 'list-calls')]) self.assertEqual(out, b'[ /ril_0 ]\n') # start call out = subprocess.check_output([os.path.join(script_dir, 'dial-number'), '12345']) self.assertEqual(out, b'Using modem /ril_0\n/ril_0/voicecall01\n') out = subprocess.check_output([os.path.join(script_dir, 'list-calls')]) self.assertIn(b'/ril_0/voicecall01', out) self.assertIn(b'LineIdentification = 12345', out) self.assertIn(b'State = dialing', out) out = subprocess.check_output([os.path.join(script_dir, 'hangup-call'), '/ril_0/voicecall01']) self.assertEqual(out, b'') # no active calls any more out = subprocess.check_output([os.path.join(script_dir, 'list-calls')]) self.assertEqual(out, b'[ /ril_0 ]\n') def test_hangup_all(self): '''multiple outgoing voice calls''' out = subprocess.check_output([os.path.join(script_dir, 'dial-number'), '12345']) self.assertEqual(out, b'Using modem /ril_0\n/ril_0/voicecall01\n') out = subprocess.check_output([os.path.join(script_dir, 'dial-number'), '54321']) self.assertEqual(out, b'Using modem /ril_0\n/ril_0/voicecall02\n') out = subprocess.check_output([os.path.join(script_dir, 'list-calls')]) self.assertIn(b'/ril_0/voicecall01', out) self.assertIn(b'/ril_0/voicecall02', out) self.assertIn(b'LineIdentification = 12345', out) self.assertIn(b'LineIdentification = 54321', out) out = subprocess.check_output([os.path.join(script_dir, 'hangup-all')]) out = subprocess.check_output([os.path.join(script_dir, 'list-calls')]) self.assertEqual(out, b'[ /ril_0 ]\n') def test_list_operators(self): '''list operators''' out = subprocess.check_output([os.path.join(script_dir, 'list-operators')], universal_newlines=True) self.assertTrue(out.startswith('[ /ril_0 ]'), out) self.assertIn('[ /ril_0/operator/op1 ]', out) self.assertIn('Status = current', out) self.assertIn('Technologies = gsm', out) self.assertIn('MobileNetworkCode = 11', out) self.assertIn('MobileCountryCode = 777', out) self.assertIn('Name = fake.tel', out) def test_get_operators_for_two_modems(self): '''Add second modem, list operators on both''' iface = 'org.ofono.NetworkRegistration' # add second modem self.obj_ofono.AddModem('sim2', {'Powered': True}) # get modem proxy, get netreg interface modem_0 = self.dbus_con.get_object('org.ofono', '/ril_0') modem_0_netreg = dbus.Interface( modem_0, dbus_interface=iface) modem_0_ops = modem_0_netreg.GetOperators() # get modem proxy, get netreg interface modem_1 = self.dbus_con.get_object('org.ofono', '/sim2') modem_1_netreg = dbus.Interface( modem_1, dbus_interface=iface) modem_1_ops = modem_1_netreg.GetOperators() self.assertIn('/ril_0/operator/op1', str(modem_0_ops)) self.assertNotIn('/sim2', str(modem_0_ops)) self.assertIn('/sim2/operator/op1', str(modem_1_ops)) self.assertNotIn('/ril_0', str(modem_1_ops)) def test_second_modem(self): '''Add a second modem''' out = subprocess.check_output([os.path.join(script_dir, 'list-modems')]) self.assertIn(b'CardIdentifier = 893581234000000000000', out) self.assertIn(b'Serial = 12345678-1234-1234-1234-000000000000', out) self.assertIn(b'SubscriberIdentity = 310150000000000', out) self.obj_ofono.AddModem('sim2', {'Powered': True}) out = subprocess.check_output([os.path.join(script_dir, 'list-modems')]) self.assertTrue(out.startswith(b'[ /ril_0 ]'), out) self.assertIn(b'[ /sim2 ]', out) self.assertIn(b'Powered = 1', out) self.assertIn(b'CardIdentifier = 893581234000000000000', out) self.assertIn(b'Serial = 12345678-1234-1234-1234-000000000000', out) self.assertIn(b'SubscriberIdentity = 310150000000000', out) self.assertIn(b'CardIdentifier = 893581234000000000001', out) self.assertIn(b'Serial = 12345678-1234-1234-1234-000000000001', out) self.assertIn(b'SubscriberIdentity = 310150000000001', out) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_polkitd.py0000644000175100001710000001146400000000000021762 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2013 Canonical Ltd.' import shutil import subprocess import sys import unittest import dbus import dbus.mainloop.glib import dbusmock dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) have_pkcheck = shutil.which('pkcheck') @unittest.skipUnless(have_pkcheck, 'pkcheck not installed') class TestPolkit(dbusmock.DBusTestCase): '''Test mocking polkitd''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): (self.p_mock, self.obj_polkitd) = self.spawn_server_template( 'polkitd', {}, stdout=subprocess.PIPE) self.dbusmock = dbus.Interface(self.obj_polkitd, dbusmock.MOCK_IFACE) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_default(self): self.check_action('org.freedesktop.test.frobnicate', False) def test_allow_unknown(self): self.dbusmock.AllowUnknown(True) self.check_action('org.freedesktop.test.frobnicate', True) self.dbusmock.AllowUnknown(False) self.check_action('org.freedesktop.test.frobnicate', False) def test_set_allowed(self): self.dbusmock.SetAllowed(['org.freedesktop.test.frobnicate', 'org.freedesktop.test.slap']) self.check_action('org.freedesktop.test.frobnicate', True) self.check_action('org.freedesktop.test.slap', True) self.check_action('org.freedesktop.test.wobble', False) def test_hanging_call(self): self.dbusmock.SimulateHang(True) self.assertFalse(self.dbusmock.HaveHangingCalls()) pkcheck = self.check_action_run('org.freedesktop.test.frobnicate') with self.assertRaises(subprocess.TimeoutExpired): pkcheck.wait(0.8) self.assertTrue(self.dbusmock.HaveHangingCalls()) pkcheck.stdout.close() pkcheck.kill() pkcheck.wait() def test_hanging_call_return(self): self.dbusmock.SetAllowed(['org.freedesktop.test.frobnicate']) self.dbusmock.SimulateHangActions(['org.freedesktop.test.frobnicate', 'org.freedesktop.test.slap']) self.assertFalse(self.dbusmock.HaveHangingCalls()) frobnicate_pkcheck = self.check_action_run( 'org.freedesktop.test.frobnicate') slap_pkcheck = self.check_action_run('org.freedesktop.test.slap') with self.assertRaises(subprocess.TimeoutExpired): frobnicate_pkcheck.wait(0.3) with self.assertRaises(subprocess.TimeoutExpired): slap_pkcheck.wait(0.3) self.assertTrue(self.dbusmock.HaveHangingCalls()) self.dbusmock.ReleaseHangingCalls() self.check_action_result(frobnicate_pkcheck, True) self.check_action_result(slap_pkcheck, False) def test_delayed_call(self): self.dbusmock.SetDelay(3) pkcheck = self.check_action_run('org.freedesktop.test.frobnicate') with self.assertRaises(subprocess.TimeoutExpired): pkcheck.wait(0.8) pkcheck.stdout.close() pkcheck.kill() pkcheck.wait() def test_delayed_call_return(self): self.dbusmock.SetDelay(1) self.dbusmock.SetAllowed(['org.freedesktop.test.frobnicate']) pkcheck = self.check_action_run('org.freedesktop.test.frobnicate') with self.assertRaises(subprocess.TimeoutExpired): pkcheck.wait(0.8) self.check_action_result(pkcheck, True) @staticmethod def check_action_run(action): # pylint: disable=consider-using-with return subprocess.Popen(['pkcheck', '--action-id', action, '--process', '123'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) def check_action_result(self, pkcheck, expect_allow): out = pkcheck.communicate()[0] if expect_allow: self.assertEqual(pkcheck.returncode, 0) self.assertEqual(out, 'test=test\n') else: self.assertNotEqual(pkcheck.returncode, 0) self.assertEqual(out, 'test=test\nNot authorized.\n') def check_action(self, action, expect_allow): self.check_action_result(self.check_action_run(action), expect_allow) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_power_profiles_daemon.py0000644000175100001710000001305000000000000024667 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Bastien Nocera' __copyright__ = '(c) 2021 Red Hat Inc.' import fcntl import os import shutil import subprocess import sys import unittest import time import dbus import dbus.mainloop.glib import dbusmock dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) have_powerprofilesctl = shutil.which('powerprofilesctl') @unittest.skipUnless(have_powerprofilesctl, 'powerprofilesctl not installed') class TestPowerProfilesDaemon(dbusmock.DBusTestCase): '''Test mocking power-profiles-daemon''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): (self.p_mock, self.obj_ppd) = self.spawn_server_template( 'power_profiles_daemon', {}, stdout=subprocess.PIPE) # set log to nonblocking flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) self.dbusmock = dbus.Interface(self.obj_ppd, dbusmock.MOCK_IFACE) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_list_profiles(self): '''List Profiles and check active profile''' out = subprocess.check_output(['powerprofilesctl'], universal_newlines=True) self.assertIn('performance:\n', out) self.assertIn('\n* balanced:\n', out) def test_change_profile(self): '''Change ActiveProfile''' subprocess.check_output(['powerprofilesctl', 'set', 'performance'], universal_newlines=True) out = subprocess.check_output(['powerprofilesctl', 'get'], universal_newlines=True) self.assertEqual(out, 'performance\n') def run_powerprofilesctl_list_holds(self): # pylint: disable=no-self-use return subprocess.check_output(['powerprofilesctl', 'list-holds'], universal_newlines=True) def test_list_holds(self): '''Test holds''' # No holds out = self.run_powerprofilesctl_list_holds() self.assertEqual(out, '') # 1 hold # pylint: disable=consider-using-with cmd = subprocess.Popen(['powerprofilesctl', 'launch', '-p', 'power-saver', '-r', 'g-s-d mock test', '-i', 'org.gnome.SettingsDaemon.Power', 'sleep', '60'], stdout=subprocess.PIPE) time.sleep(0.3) out = self.run_powerprofilesctl_list_holds() self.assertEqual(out, 'Hold:\n' ' Profile: power-saver\n' ' Application ID:' ' org.gnome.SettingsDaemon.Power\n' ' Reason: g-s-d mock test\n') # 2 holds # pylint: disable=consider-using-with cmd2 = subprocess.Popen(['powerprofilesctl', 'launch', '-p', 'performance', '-r', 'running some game', '-i', 'com.game.Game', 'sleep', '60'], stdout=subprocess.PIPE) out = None timeout = 2.0 while timeout > 0: time.sleep(0.1) timeout -= 0.1 out = self.run_powerprofilesctl_list_holds() if out != '': break else: self.fail('could not list holds') self.assertEqual(out, 'Hold:\n' ' Profile: power-saver\n' ' Application ID:' ' org.gnome.SettingsDaemon.Power\n' ' Reason: g-s-d mock test\n\n' 'Hold:\n' ' Profile: performance\n' ' Application ID: com.game.Game\n' ' Reason: running some game\n') cmd.stdout.close() cmd.terminate() cmd.wait() cmd2.stdout.close() cmd2.terminate() cmd2.wait() def test_release_hold(self): '''Test release holds''' # No holds out = self.run_powerprofilesctl_list_holds() self.assertEqual(out, '') # hold profile cookie = self.obj_ppd.HoldProfile('performance', 'release test', 'com.test.Test') out = self.run_powerprofilesctl_list_holds() self.assertEqual(out, 'Hold:\n' ' Profile: performance\n' ' Application ID: com.test.Test\n' ' Reason: release test\n') # release profile self.obj_ppd.ReleaseProfile(cookie) time.sleep(0.3) out = self.run_powerprofilesctl_list_holds() self.assertEqual(out, '') if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_systemd.py0000644000175100001710000000675700000000000022015 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Jonas Ådahl' __copyright__ = '(c) 2021 Red Hat' import subprocess import sys import unittest import dbus import dbus.mainloop.glib from gi.repository import GLib import dbusmock dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) class TestSystemd(dbusmock.DBusTestCase): '''Test mocking systemd''' @classmethod def setUpClass(cls): cls.start_session_bus() cls.start_system_bus() cls.session_bus = cls.get_dbus(False) cls.system_bus = cls.get_dbus(True) def setUp(self): self.p_mock = None def tearDown(self): if self.p_mock: self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def _assert_unit_property(self, unit_obj, name, expect): value = unit_obj.Get('org.freedesktop.systemd1.Unit', name) self.assertEqual(str(value), expect) def _test_base(self, bus, system_bus=True): dummy_service = 'dummy-dbusmock.service' (self.p_mock, obj_systemd) = self.spawn_server_template('systemd', {}, subprocess.PIPE, system_bus=system_bus) systemd_mock = dbus.Interface(obj_systemd, dbusmock.MOCK_IFACE) systemd_mock.AddMockUnit(dummy_service) main_loop = GLib.MainLoop() removed_jobs = [] def catch_job_removed(*args, **kwargs): if (kwargs['interface'] == 'org.freedesktop.systemd1.Manager' and kwargs['member'] == 'JobRemoved'): job_path = str(args[1]) removed_jobs.append(job_path) main_loop.quit() def wait_for_job(path): while True: main_loop.run() if path in removed_jobs: break bus.add_signal_receiver(catch_job_removed, interface_keyword='interface', path_keyword='path', member_keyword='member') unit_path = obj_systemd.GetUnit(dummy_service) unit_obj = bus.get_object('org.freedesktop.systemd1', unit_path) self._assert_unit_property(unit_obj, 'Id', dummy_service) self._assert_unit_property(unit_obj, 'LoadState', 'loaded') self._assert_unit_property(unit_obj, 'ActiveState', 'inactive') job_path = obj_systemd.StartUnit(dummy_service, 'fail') wait_for_job(job_path) self._assert_unit_property(unit_obj, 'ActiveState', 'active') job_path = obj_systemd.StopUnit(dummy_service, 'fail') wait_for_job(job_path) self._assert_unit_property(unit_obj, 'ActiveState', 'inactive') self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() self.p_mock = None def test_user(self): self._test_base(self.session_bus, system_bus=False) def test_system(self): self._test_base(self.system_bus, system_bus=True) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_timedated.py0000644000175100001710000000570100000000000022251 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Iain Lane' __copyright__ = '(c) 2013 Canonical Ltd.' import os import shutil import subprocess import sys import unittest import dbusmock # timedatectl keeps changing its CLI output TIMEDATECTL_NTP_LABEL = '(NTP enabled|synchronized|systemd-timesyncd.service active)' have_timedatectl = shutil.which('timedatectl') @unittest.skipUnless(have_timedatectl, 'timedatectl not installed') @unittest.skipUnless(os.path.exists('/run/systemd/system'), '/run/systemd/system does not exist') class TestTimedated(dbusmock.DBusTestCase): '''Test mocking timedated''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): (self.p_mock, _) = self.spawn_server_template( 'timedated', {}, stdout=subprocess.PIPE) self.obj_timedated = self.dbus_con.get_object( 'org.freedesktop.timedate1', '/org/freedesktop/timedate1') def tearDown(self): if self.p_mock: self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def run_timedatectl(self): # pylint: disable=no-self-use return subprocess.check_output(['timedatectl'], universal_newlines=True) def test_default_timezone(self): out = self.run_timedatectl() # timedatectl doesn't get the timezone offset information over dbus so # we can't mock that. self.assertRegex(out, 'Time *zone: Etc/Utc') def test_changing_timezone(self): self.obj_timedated.SetTimezone('Africa/Johannesburg', False) out = self.run_timedatectl() # timedatectl doesn't get the timezone offset information over dbus so # we can't mock that. self.assertRegex(out, 'Time *zone: Africa/Johannesburg') def test_default_ntp(self): out = self.run_timedatectl() self.assertRegex(out, f'{TIMEDATECTL_NTP_LABEL}: yes') def test_changing_ntp(self): self.obj_timedated.SetNTP(False, False) out = self.run_timedatectl() self.assertRegex(out, f'{TIMEDATECTL_NTP_LABEL}: no') def test_default_local_rtc(self): out = self.run_timedatectl() self.assertRegex(out, 'RTC in local TZ: no') def test_changing_local_rtc(self): self.obj_timedated.SetLocalRTC(True, False, False) out = self.run_timedatectl() self.assertRegex(out, 'RTC in local TZ: yes') if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner( stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_upower.py0000644000175100001710000002047300000000000021635 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Martin Pitt' __copyright__ = '(c) 2012 Canonical Ltd.' import fcntl import os import shutil import subprocess import sys import time import unittest import tracemalloc import dbus import dbusmock UP_DEVICE_LEVEL_UNKNOWN = 0 UP_DEVICE_LEVEL_NONE = 1 tracemalloc.start(25) have_upower = shutil.which('upower') @unittest.skipUnless(have_upower, 'upower not installed') class TestUPower(dbusmock.DBusTestCase): '''Test mocking upowerd''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): (self.p_mock, self.obj_upower) = self.spawn_server_template( 'upower', { 'OnBattery': True, 'HibernateAllowed': False, 'GetCriticalAction': 'Suspend', }, stdout=subprocess.PIPE) # set log to nonblocking flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) self.dbusmock = dbus.Interface(self.obj_upower, dbusmock.MOCK_IFACE) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_no_devices(self): out = subprocess.check_output(['upower', '--dump'], universal_newlines=True) self.assertIn('/DisplayDevice\n', out) # should not have any other device for line in out.splitlines(): if line.endswith('/DisplayDevice'): continue self.assertNotIn('Device', line) self.assertRegex(out, 'on-battery:\\s+yes') self.assertRegex(out, 'lid-is-present:\\s+yes') self.assertRegex(out, 'daemon-version:\\s+0.99') self.assertRegex(out, 'critical-action:\\s+Suspend') self.assertNotIn('can-suspend', out) def test_one_ac(self): path = self.dbusmock.AddAC('mock_AC', 'Mock AC') self.assertEqual(path, '/org/freedesktop/UPower/devices/mock_AC') self.assertRegex(self.p_mock.stdout.read(), b'emit /org/freedesktop/UPower org.freedesktop.UPower.DeviceAdded ' b'"/org/freedesktop/UPower/devices/mock_AC"\n') out = subprocess.check_output(['upower', '--dump'], universal_newlines=True) self.assertRegex(out, 'Device: ' + path) # note, Add* is not magic: this just adds an object, not change # properties self.assertRegex(out, 'on-battery:\\s+yes') self.assertRegex(out, 'lid-is-present:\\s+yes') # print('--------- out --------\n%s\n------------' % out) with subprocess.Popen(['upower', '--monitor-detail'], stdout=subprocess.PIPE, universal_newlines=True) as mon: time.sleep(0.3) self.dbusmock.SetDeviceProperties(path, { 'PowerSupply': dbus.Boolean(True, variant_level=1) }) time.sleep(0.2) mon.terminate() out = mon.communicate()[0] self.assertRegex(out, 'device changed:\\s+' + path) # print('--------- monitor out --------\n%s\n------------' % out) def test_discharging_battery(self): path = self.dbusmock.AddDischargingBattery('mock_BAT', 'Mock Battery', 30.0, 1200) self.assertEqual(path, '/org/freedesktop/UPower/devices/mock_BAT') self.assertRegex(self.p_mock.stdout.read(), b'emit /org/freedesktop/UPower org.freedesktop.UPower.DeviceAdded ' b'"/org/freedesktop/UPower/devices/mock_BAT"\n') out = subprocess.check_output(['upower', '--dump'], universal_newlines=True) self.assertRegex(out, 'Device: ' + path) # note, Add* is not magic: this just adds an object, not change # properties self.assertRegex(out, 'on-battery:\\s+yes') self.assertRegex(out, 'lid-is-present:\\s+yes') self.assertRegex(out, ' present:\\s+yes') self.assertRegex(out, ' percentage:\\s+30%') self.assertRegex(out, ' time to empty:\\s+20.0 min') self.assertRegex(out, ' state:\\s+discharging') def test_charging_battery(self): path = self.dbusmock.AddChargingBattery('mock_BAT', 'Mock Battery', 30.0, 1200) self.assertEqual(path, '/org/freedesktop/UPower/devices/mock_BAT') self.assertRegex(self.p_mock.stdout.read(), b'emit /org/freedesktop/UPower org.freedesktop.UPower.DeviceAdded ' b'"/org/freedesktop/UPower/devices/mock_BAT"\n') out = subprocess.check_output(['upower', '--dump'], universal_newlines=True) self.assertRegex(out, 'Device: ' + path) # note, Add* is not magic: this just adds an object, not change # properties self.assertRegex(out, 'on-battery:\\s+yes') self.assertRegex(out, 'lid-is-present:\\s+yes') self.assertRegex(out, ' present:\\s+yes') self.assertRegex(out, ' percentage:\\s+30%') self.assertRegex(out, ' time to full:\\s+20.0 min') self.assertRegex(out, ' state:\\s+charging') def test_enumerate(self): self.dbusmock.AddAC('mock_AC', 'Mock AC') self.assertEqual(self.obj_upower.EnumerateDevices(), ['/org/freedesktop/UPower/devices/mock_AC']) def test_display_device_default(self): path = self.obj_upower.GetDisplayDevice() self.assertEqual(path, '/org/freedesktop/UPower/devices/DisplayDevice') display_dev = self.dbus_con.get_object('org.freedesktop.UPower', path) props = display_dev.GetAll('org.freedesktop.UPower.Device') # http://cgit.freedesktop.org/upower/tree/src/org.freedesktop.UPower.xml # defines the properties which are defined self.assertEqual( set(props.keys()), set(['Type', 'State', 'Percentage', 'Energy', 'EnergyFull', 'EnergyRate', 'TimeToEmpty', 'TimeToFull', 'IsPresent', 'IconName', 'WarningLevel'])) # not set up by default, so should not present self.assertEqual(props['IsPresent'], False) self.assertEqual(props['IconName'], '') self.assertEqual(props['WarningLevel'], UP_DEVICE_LEVEL_NONE) def test_setup_display_device(self): self.dbusmock.SetupDisplayDevice(2, 1, 50.0, 40.0, 80.0, 2.5, 3600, 1800, True, 'half-battery', 3) path = self.obj_upower.GetDisplayDevice() display_dev = self.dbus_con.get_object('org.freedesktop.UPower', path) props = display_dev.GetAll('org.freedesktop.UPower.Device') # just some spot-checks, check all the values from upower -d self.assertEqual(props['Type'], 2) self.assertEqual(props['Percentage'], 50.0) self.assertEqual(props['WarningLevel'], 3) env = os.environ.copy() env['LC_ALL'] = 'C' try: del env['LANGUAGE'] except KeyError: pass out = subprocess.check_output(['upower', '--dump'], universal_newlines=True, env=env) self.assertIn('/DisplayDevice\n', out) self.assertIn(' battery\n', out) # type self.assertRegex(out, r'state:\s+charging') self.assertRegex(out, r'percentage:\s+50%') self.assertRegex(out, r'energy:\s+40 Wh') self.assertRegex(out, r'energy-full:\s+80 Wh') self.assertRegex(out, r'energy-rate:\s+2.5 W') self.assertRegex(out, r'time to empty:\s+1\.0 hours') self.assertRegex(out, r'time to full:\s+30\.0 minutes') self.assertRegex(out, r'present:\s+yes') self.assertRegex(out, r"icon-name:\s+'half-battery'") self.assertRegex(out, r'warning-level:\s+low') if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649137859.0 python-dbusmock-0.27.5/tests/test_urfkill.py0000644000175100001710000001163700000000000021766 0ustar00runnerdocker00000000000000#!/usr/bin/python3 # 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 3 of the License, or (at your option) any # later version. See http://www.gnu.org/copyleft/lgpl.html for the full text # of the license. __author__ = 'Jussi Pakkanen' __copyright__ = '(c) 2015 Canonical Ltd.' import unittest import sys import subprocess import os import fcntl import dbus import dbusmock def _get_urfkill_objects(): bus = dbus.SystemBus() remote_object = bus.get_object('org.freedesktop.URfkill', '/org/freedesktop/URfkill') iface = dbus.Interface(remote_object, 'org.freedesktop.URfkill') return (remote_object, iface) class TestURfkill(dbusmock.DBusTestCase): '''Test mocked URfkill''' @classmethod def setUpClass(cls): cls.start_system_bus() cls.dbus_con = cls.get_dbus(True) def setUp(self): (self.p_mock, self.obj_urfkill) = self.spawn_server_template( 'urfkill', {}, stdout=subprocess.PIPE) # set log to nonblocking flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL) fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) self.dbusmock = dbus.Interface(self.obj_urfkill, dbusmock.MOCK_IFACE) def tearDown(self): self.p_mock.stdout.close() self.p_mock.terminate() self.p_mock.wait() def test_mainobject(self): (remote_object, iface) = _get_urfkill_objects() self.assertFalse(iface.IsFlightMode()) propiface = dbus.Interface(remote_object, 'org.freedesktop.DBus.Properties') version = propiface.Get('org.freedesktop.URfkill', 'DaemonVersion') self.assertEqual(version, '0.6.0') def test_subobjects(self): bus = dbus.SystemBus() individual_objects = ['BLUETOOTH', 'FM', 'GPS', 'NFC', 'UWB', 'WIMAX', 'WLAN', 'WWAN'] for i in individual_objects: path = '/org/freedesktop/URfkill/' + i remote_object = bus.get_object('org.freedesktop.URfkill', path) propiface = dbus.Interface(remote_object, 'org.freedesktop.DBus.Properties') state = propiface.Get('org.freedesktop.URfkill.Killswitch', 'state') self.assertEqual(state, 0) def test_block(self): bus = dbus.SystemBus() (_, iface) = _get_urfkill_objects() property_object = bus.get_object('org.freedesktop.URfkill', '/org/freedesktop/URfkill/WLAN') propiface = dbus.Interface(property_object, 'org.freedesktop.DBus.Properties') self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 0) self.assertTrue(iface.Block(1, True)) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 1) self.assertTrue(iface.Block(1, False)) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 0) # 99 is an unknown type to the mock, so it should return false. self.assertFalse(iface.Block(99, False)) def test_flightmode(self): bus = dbus.SystemBus() (_, iface) = _get_urfkill_objects() property_object = bus.get_object('org.freedesktop.URfkill', '/org/freedesktop/URfkill/WLAN') propiface = dbus.Interface(property_object, 'org.freedesktop.DBus.Properties') self.assertFalse(iface.IsFlightMode()) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 0) iface.FlightMode(True) self.assertTrue(iface.IsFlightMode()) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 1) iface.FlightMode(False) self.assertFalse(iface.IsFlightMode()) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 0) def test_flightmode_restore(self): # An interface that was blocked remains blocked once flightmode is removed. bus = dbus.SystemBus() (_, iface) = _get_urfkill_objects() property_object = bus.get_object('org.freedesktop.URfkill', '/org/freedesktop/URfkill/WLAN') propiface = dbus.Interface(property_object, 'org.freedesktop.DBus.Properties') self.assertFalse(iface.IsFlightMode()) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 0) iface.Block(1, True) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 1) iface.FlightMode(True) self.assertTrue(iface.IsFlightMode()) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 1) iface.FlightMode(False) self.assertFalse(iface.IsFlightMode()) self.assertEqual(propiface.Get('org.freedesktop.URfkill.Killswitch', 'state'), 1) if __name__ == '__main__': # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout))