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.

Infopipe Example
@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:

Accessing the monitor
@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 for dbus-send.

  • sssd_test_framework.utils.dbus.types.DBUSType.parse(): Parses the string resulting from an execution of dbus-send and 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.

Declaring an array of unit32
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.DBUSType

  • sssd_test_framework.utils.dbus.types.DBUSTypeBoolean

  • sssd_test_framework.utils.dbus.types.DBUSTypeString

  • sssd_test_framework.utils.dbus.types.DBUSTypeObjectPath

  • sssd_test_framework.utils.dbus.types.DBUSTypeInteger

  • sssd_test_framework.utils.dbus.types.DBUSTypeByte

  • sssd_test_framework.utils.dbus.types.DBUSTypeInt16

  • sssd_test_framework.utils.dbus.types.DBUSTypeInt32

  • sssd_test_framework.utils.dbus.types.DBUSTypeInt64

  • sssd_test_framework.utils.dbus.types.DBUSTypeUInt16

  • sssd_test_framework.utils.dbus.types.DBUSTypeUInt32

  • sssd_test_framework.utils.dbus.types.DBUSTypeUInt64

  • sssd_test_framework.utils.dbus.types.DBUSTypeDouble

  • sssd_test_framework.utils.dbus.types.DBUSTypeContainer

  • sssd_test_framework.utils.dbus.types.DBUSTypeArray

  • sssd_test_framework.utils.dbus.types.DBUSTypeDict

  • sssd_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.