Testing D-Bus Services
Infopipe
Access to the Infopipe services is achieved through the
sssd_test_framework.roles.client.Client class. It provides the
sssd_test_framework.roles.client.Client.infopipe() method that
returns a pre-instantiated object of class
sssd_test_framework.utils.sbus.DBUSDestination, which gives you
direct access to the Infopipe destination.
With the destination, it is possible to get the different objects it provides.
Each object has its own object path. These objects are obtained by calling
the destination’s sssd_test_framework.utils.sbus.DBUSDestination.getObject()
method. The object path must be passed as argument. A list containing all the
available object paths can be retrieved with the method
sssd_test_framework.utils.sbus.DBUSDestination.getObjectPaths().
Note
Passing an object path that doesn’t exist will not generate an error nor
raise an exception. It will, instead, create a valid object with no attributes.
This is a strange behavior of Introspection.
Once you have one of these objects, you can access other D-BUS sub-objects provided by that destination by simply treating them as attributes of the parent object. Methods and properties are accessed in the same way.
Note
Accessing an attribute that can’t be mapped to a subobject, attribute or
method, will raise an AttributeError exception.
In the following example, there is an LDAP domain called test.
@pytest.mark.topology(KnownTopology.LDAP)
def test_infopipe__GetUserProperties(client: Client, provider: GenericProvider):
provider.user("user-1").add(uid=10001, gid=20001)
client.sssd.start()
users = client.ifp.getObject("/org/freedesktop/sssd/infopipe/Users");
user_path = users.FindByName("user-1")
assert "/org/freedesktop/sssd/infopipe/Users/test/10001" == user_path
user = client.ifp.getObject(user_path)
uid = user.Get("org.freedesktop.sssd.infopipe.Users.User", "uidNumber")
assert uid == 10001
gid = user.gidNumber
assert gid == 20001
props = user.GetAll("org.freedesktop.sssd.infopipe.Users.User")
assert props["uidNumber"] == 10001
assert props["gidNumber"] == 20001
assert props["name"] == "user-1"
assert props["homeDirectory"] == "/home/user-1"
Internals
Although sssd_test_framework.roles.client.Client provides the
sssd_test_framework.roles.client.Client.infopipe() method, the framework
allows you to access any D-Bus destination. To achieve that you need to
instantiate the sssd_test_framework.utils.sbus.DBUSDestination class,
providing the MultihostHost where the D-Bus services are running, the destination
(the application) to contact and the bus to connect to.
The bus can be the bus’ path as a string, or one of the predefined buses:
@pytest.mark.topology(KnownTopologyGroup.AnyProvider)
def test_example__monitor(client: Client):
client.sssd.start()
monitor = DBUSDestination(client.host, dest="sssd.monitor", bus=DBUSKnownBus.MONITOR)
sssd = monitor.getObject(objpath="/sssd")
res = sssd.debug_level
assert res == 0xFFF0
sssd.debug_level = 0x0070
A Note On Type Conversion
All methods and properties accept and return Python types. Internally they are converted to some specific classes helping to treat them and map them to D-Bus types.
Objects of these types represent values that are passed to/from the methods and properties. Their type is given by the class. For instance, DBUSTypeString is a D-Bus string.
These classes are all subclasses of the abstract class
sssd_test_framework.utils.dbus.types.DBUSType and they provide the
following methods:
sssd_test_framework.utils.dbus.types.DBUSType.value: A property to read and set the (Python) value.sssd_test_framework.utils.dbus.types.DBUSType.mimic(): a method to copy itself without copying the value while maintaining the structure (subtypes).sssd_test_framework.utils.dbus.types.DBUSType.param(): The string representation of the value in a format suitable to be used as a parameter fordbus-send.sssd_test_framework.utils.dbus.types.DBUSType.parse(): Parses the string resulting from an execution ofdbus-sendand set the value to the object.
Basic types (integers, strings, booleans, etc.) are subclasses of the
sssd_test_framework.utils.dbus.types.DBUSTypeBasic abstract class.
Container types – that is, the subclasses of the abstract class
sssd_test_framework.utils.dbus.types.DBUSTypeContainer – accept a
parameter for their constructors, another
sssd_test_framework.utils.dbus.types.DBUSType object of the expected type.
s = "array [ uint32 1 uint32 2 uint32 3 ]"
a = DBUSTypeArray(DBUSTypeUInt32())
a.parse(DBUSResult(s))
print(a.value)
[1, 2, 3]
In some cases it may not be possible to know in advance the type of the elements
of a container type. In that case, no object is passed to the constructor. The
type will be guessed while parsing the result from dbus-send.
Note
dbus-send doesn’t explain very well how container types are combined as
parameters, and so far we didn’t use them. So we might have to adapt the
results of param() if they are ever used.
Note
Using instrospection it is possible to get the methods and properties
signatures. Nevertheless, the signature for variant types does not include
the type of the contained type, as it does for the arrays and dictionaries.
Because of this, it is not possible to know in advance which type to expect
and will have to be guessed while parsing.
Implemented Types
The following classes are already implemented.
sssd_test_framework.utils.dbus.types.DBUSTypesssd_test_framework.utils.dbus.types.DBUSTypeBooleansssd_test_framework.utils.dbus.types.DBUSTypeStringsssd_test_framework.utils.dbus.types.DBUSTypeObjectPathsssd_test_framework.utils.dbus.types.DBUSTypeIntegersssd_test_framework.utils.dbus.types.DBUSTypeBytesssd_test_framework.utils.dbus.types.DBUSTypeInt16sssd_test_framework.utils.dbus.types.DBUSTypeInt32sssd_test_framework.utils.dbus.types.DBUSTypeInt64sssd_test_framework.utils.dbus.types.DBUSTypeUInt16sssd_test_framework.utils.dbus.types.DBUSTypeUInt32sssd_test_framework.utils.dbus.types.DBUSTypeUInt64sssd_test_framework.utils.dbus.types.DBUSTypeDoublesssd_test_framework.utils.dbus.types.DBUSTypeContainersssd_test_framework.utils.dbus.types.DBUSTypeArraysssd_test_framework.utils.dbus.types.DBUSTypeDictsssd_test_framework.utils.dbus.types.DBUSTypeVariant
Note
Although the D-Bus specification considers dict entry a separate type,
we didn’t implement it as such because there is no use case for it outside
of an array, in which case the array becomes a dictionary.
Not Implemented Types
Some other classes were not implemented because they are not accepted by
dbus-send:
signature
UNIX FD
struct
Helper Classes
Class sssd_test_framework.utils.dbus.types.DBUSSignatureReader provides
a single class method
sssd_test_framework.utils.dbus.types.DBUSSignatureReader.read()
used to read a method or property signature from a string and generate the
corresponding sssd_test_framework.utils.dbus.types.DBUSType objects
required for the method or property.